Compare commits

..

7 Commits

Author SHA1 Message Date
fiatjaf
cc38176820 remove some unused fields from nip11. 2025-07-07 10:48:54 -03:00
fiatjaf_
7555a93f90 remove mentions to uuids (#1920) 2025-05-13 08:01:06 -03:00
AsaiToshiya
873afc5fb8 add follow packs to README. 2025-05-09 20:07:23 +09:00
fiatjaf_
564814ac7d add follow packs to nip51 (#1898) 2025-05-08 22:51:09 -03:00
Cody Tseng
ccd02f2612 NIP-51: favorite relays (#1848) 2025-05-08 22:50:12 -03:00
Vitor Pamplona
ebfcd72a8d Using yaml to fix NIP-01 JSON formatting (#1910) 2025-05-05 21:11:52 -03:00
Vitor Pamplona
86f0da716f NIP-17 json formatting fix, text cleanup and more precise language (#1909) 2025-05-05 06:36:20 -07:00
7 changed files with 334 additions and 262 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:
```jsonc
```yaml
{
"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:
```json
```yaml
{
"ids": <a list of event ids>,
"authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,

238
11.md
View File

@@ -20,11 +20,9 @@ When a relay receives an HTTP(s) request with an `Accept` header of `application
"contact": <administrative alternate contact>,
"supported_nips": <a list of NIP numbers supported by the relay>,
"software": <string identifying relay software URL>,
"version": <string version identifier>
"privacy_policy": <a link to a text file describing the relay's privacy policy>,
"version": <string version identifier>,
"terms_of_service": <a link to a text file describing the relay's term of service>,
}
```
@@ -78,15 +76,9 @@ The relay server implementation MAY be provided in the `software` attribute. If
The relay MAY choose to publish its software version as a string attribute. The string format is defined by the relay implementation. It is recommended this be a version number or commit identifier.
### Privacy Policy
The relay owner/admin MAY choose to link to a privacy policy document, which describes how the relay utilizes user data. Data collection, data usage, data retention, monetization of data, and third party data sharing SHOULD be included.
### Terms of Service
The relay owner/admin MAY choose to link to a terms of service document.
The relay MAY choose to publish its software version as a string attribute. The string format is defined by the relay implementation. It is recommended this be a version number or commit identifier.
Extra Fields
------------
@@ -162,113 +154,7 @@ a specific niche kind or content. Normal anti-spam heuristics, for example, do n
- `created_at_upper_limit`: 'created_at' upper limit
- `default_limit`: The maximum returned events if you send a filter with the limit set to 0.
### Event Retention
There may be a cost associated with storing data forever, so relays
may wish to state retention times. The values stated here are defaults
for unauthenticated users and visitors. Paid users would likely have
other policies.
Retention times are given in seconds, with `null` indicating infinity.
If zero is provided, this means the event will not be stored at
all, and preferably an error will be provided when those are received.
```jsonc
{
"retention": [
{"kinds": [0, 1, [5, 7], [40, 49]], "time": 3600},
{"kinds": [[40000, 49999]], "time": 100},
{"kinds": [[30000, 39999]], "count": 1000},
{"time": 3600, "count": 10000}
],
// other fields...
}
```
`retention` is a list of specifications: each will apply to either all kinds, or
a subset of kinds. Ranges may be specified for the kind field as a tuple of inclusive
start and end values. Events of indicated kind (or all) are then limited to a `count`
and/or time period.
It is possible to effectively blacklist Nostr-based protocols that rely on
a specific `kind` number, by giving a retention time of zero for those `kind` values.
While that is unfortunate, it does allow clients to discover servers that will
support their protocol quickly via a single HTTP fetch.
There is no need to specify retention times for _ephemeral events_ since they are not retained.
### Content Limitations
Some relays may be governed by the arbitrary laws of a nation state. This
may limit what content can be stored in clear-text on those relays. All
clients are encouraged to use encryption to work around this limitation.
It is not possible to describe the limitations of each country's laws
and policies which themselves are typically vague and constantly shifting.
Therefore, this field allows the relay operator to indicate which
countries' laws might end up being enforced on them, and then
indirectly on their users' content.
Users should be able to avoid relays in countries they don't like,
and/or select relays in more favorable zones. Exposing this
flexibility is up to the client software.
```jsonc
{
"relay_countries": [ "CA", "US" ],
// other fields...
}
```
- `relay_countries`: a list of two-level ISO country codes (ISO 3166-1 alpha-2) whose
laws and policies may affect this relay. `EU` may be used for European Union countries. A `*` can be used for global relays.
Remember that a relay may be hosted in a country which is not the
country of the legal entities who own the relay, so it's very
likely a number of countries are involved.
### Community Preferences
For public text notes at least, a relay may try to foster a
local community. This would encourage users to follow the global
feed on that relay, in addition to their usual individual follows.
To support this goal, relays MAY specify some of the following values.
```jsonc
{
"language_tags": ["en", "en-419"],
"tags": ["sfw-only", "bitcoin-only", "anime"],
"posting_policy": "https://example.com/posting-policy.html",
// other fields...
}
```
- `language_tags` is an ordered list
of [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) indicating
the major languages spoken on the relay. A `*` can be used for global relays.
- `tags` is a list of limitations on the topics to be discussed.
For example `sfw-only` indicates that only "Safe For Work" content
is encouraged on this relay. This relies on assumptions of what the
"work" "community" feels "safe" talking about. In time, a common
set of tags may emerge that allow users to find relays that suit
their needs, and client software will be able to parse these tags easily.
The `bitcoin-only` tag indicates that any *altcoin*, *"crypto"* or *blockchain*
comments will be ridiculed without mercy.
- `posting_policy` is a link to a human-readable page which specifies the
community policies for the relay. In cases where `sfw-only` is True, it's
important to link to a page which gets into the specifics of your posting policy.
The `description` field should be used to describe your community
goals and values, in brief. The `posting_policy` is for additional
detail and legal terms. Use the `tags` field to signify limitations
on content, or topics to be discussed, which could be machine
processed by appropriate client software.
- `default_limit`: The maximum returned events if you send a filter without a limit.
### Pay-to-Relay
@@ -288,82 +174,68 @@ Relays that require payments may want to expose their fee schedules.
### Examples
As of 25 March 2025 the following command provided these results:
```bash
curl -H "Accept: application/nostr+json" https://jellyfish.land | jq
```
```json
```yaml
~> curl -H "Accept: application/nostr+json" https://nostr.wine | jq
{
"name": "JellyFish",
"description": "Stay Immortal!",
"banner": "https://image.nostr.build/7fdefea2dec1f1ec25b8ce69362566c13b2b7f13f1726c2e4584f05f64f62496.jpg",
"pubkey": "bf2bee5281149c7c350f5d12ae32f514c7864ff10805182f4178538c2c421007",
"contact": "hi@dezh.tech",
"software": "https://github.com/dezh-tech/immortal",
"supported_nips": [
1,
9,
11,
13,
17,
40,
42,
59,
62,
70
],
"version": "immortal - 0.0.9",
"relay_countries": [
"*"
],
"language_tags": [
"*"
],
"tags": [],
"posting_policy": "https://jellyfish.land/tos.txt",
"payments_url": "https://jellyfish.land/relay",
"icon": "https://image.nostr.build/2547e9ec4b23589e09bc7071e0806c3d4293f76284c58ff331a64bce978aaee8.jpg",
"retention": [],
"contact": "wino@nostr.wine",
"description": "A paid nostr relay for wine enthusiasts and everyone else.",
"fees": {
"subscription": [
"admission": [
{
"amount": 3000,
"period": 2628003,
"unit": "sats"
},
{
"amount": 8000,
"period": 7884009,
"unit": "sats"
},
{
"amount": 15000,
"period": 15768018,
"unit": "sats"
},
{
"amount": 28000,
"period": 31536036,
"unit": "sats"
"amount": 18888000,
"unit": "msats"
}
]
},
"icon": "https://image.nostr.build/30acdce4a81926f386622a07343228ae99fa68d012d54c538c0b2129dffe400c.png",
"limitation": {
"auth_required": false,
"max_message_length": 70000,
"max_subid_length": 256,
"max_subscriptions": 350,
"created_at_lower_limit": 94608000,
"created_at_upper_limit": 300,
"max_event_tags": 4000,
"max_limit": 1000,
"max_message_length": 524288,
"max_subid_length": 71,
"max_subscriptions": 50,
"min_pow_difficulty": 0,
"payment_required": true,
"restricted_writes": true,
"max_event_tags": 2000,
"max_content_length": 70000,
"created_at_lower_limit": 0,
"created_at_upper_limit": 2147483647,
"default_limit": 500,
"max_limit": 5000
"restricted_writes": true
},
"name": "nostr.wine",
"payments_url": "https://nostr.wine/invoices",
"pubkey": "4918eb332a41b71ba9a74b1dc64276cfff592e55107b93baae38af3520e55975",
"software": "https://nostr.wine",
"supported_nips": [ 1, 2, 4, 9, 11, 40, 42, 50, 70, 77 ],
"terms_of_service": "https://nostr.wine/terms",
"version": "0.3.3"
}
~> curl -H "Accept: application/nostr+json" https://nostr.land | jq
{
"description": "[✨ NFDB] nostr.land family of relays (fi-01 [tiger])",
"name": "[✨ NFDB] nostr.land",
"pubkey": "52b4a076bcbbbdc3a1aefa3735816cf74993b1b8db202b01c883c58be7fad8bd",
"software": "NFDB",
"icon": "https://i.nostr.build/b3thno790aodH8lE.jpg",
"supported_nips": [ 1, 2, 4, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 42, 44, 46, 47, 48, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 65, 68, 69, 71, 72, 73, 75, 78, 84, 88, 89, 90, 92, 99 ],
"version": "1.0.0",
"limitation": {
"payment_required": true,
"max_message_length": 65535,
"max_event_tags": 2000,
"max_subscriptions": 200,
"auth_required": false
},
"payments_url": "https://nostr.land",
"fees": {
"subscription": [
{
"amount": 4000000,
"unit": "msats",
"period": 2592000
}
]
},
"terms_of_service": "https://nostr.land/terms"
}
```

75
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,21 +65,22 @@ 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.).
- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms may include `aes-gcm`, `chacha20-poly1305`,`aes-cbc` etc.
- `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`.
- `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 file.
- `size` (optional) size of file in bytes
- `dim` (optional) size of the file in pixels in the form `<width>x<height>`
- `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>`
- `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
- `fallback` (optional) zero or more fallback file sources in case `url` fails (encrypted with the same key, nonce)
Just like kind 14, kind `15`s MUST never be signed.
Just like kind `14`, kind `15`s MUST never be signed.
## Chat Rooms
@@ -87,34 +88,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 thread 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 chat room 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.
```jsonc
```js
{
"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>"
}
```
@@ -124,7 +125,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

5
51.md
View File

@@ -22,6 +22,7 @@ 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) |
@@ -31,7 +32,9 @@ 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) |
@@ -57,6 +60,8 @@ 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) |
| 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

12
52.md
View File

@@ -25,7 +25,7 @@ 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.
* `d` (required) a short unique string identifier. Generated by the client creating the calendar event.
* `title` (required) title of the calendar 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`.
@@ -46,7 +46,7 @@ The following tags are deprecated:
"kind": 31922,
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["d", "<random-identifier>"],
["title", "<title of calendar event>"],
@@ -84,7 +84,7 @@ The format uses an _addressable event_ kind `31923`.
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.
* `d` (required) a short unique string identifier. 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.
@@ -110,7 +110,7 @@ The following tags are deprecated:
"kind": 31923,
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["d", "<random-identifier>"],
["title", "<title of calendar event>"],
["summary", "<brief description of the calendar event>"],
@@ -167,7 +167,7 @@ The format uses a custom replaceable list of kind `31924` with a list of tags as
"kind": 31924,
"content": "<description of calendar>",
"tags": [
["d", "<UUID>"],
["d", "<random-identifier>"],
["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>"]
@@ -215,7 +215,7 @@ The list of tags are as follows:
"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", "<UUID>"],
["d", "<random-identifier>"],
["status", "<accepted/declined/tentative>"],
["fb", "<free/busy>"],
["p", "<hex pubkey of kind 31922 or 31923 event>", "<optional recommended relay URL>"]

234
90.md
View File

@@ -1,44 +1,230 @@
NIP-90
======
Data Vending Machines
---------------------
Data Vending Machine
--------------------
`draft` `optional`
This NIP is a documentation effort and a hub for incentivizing the proliferation of DVMs on Nostr.
This NIP defines the interaction between customers and Service Providers for performing on-demand computation.
A DVM is anything that can be interacted with programmatically, either to benefit Nostr clients in general or making use of the Nostr ecosystem.
Money in, data out.
There are 3 types of DVM-related interaction:
## Kinds
This NIP reserves the range `5000-7000` for data vending machine use.
- 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.
| Kind | Description |
| ---- | ----------- |
| 5000-5999 | Job request kinds |
| 6000-6999 | Job result |
| 7000 | Job feedback |
To qualify as a DVM entity a thing must:
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`).
- 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.
Job request types are defined [separately](https://github.com/nostr-protocol/data-vending-machines/tree/master/kinds).
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:
## 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.
- 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.
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.
### DVMs
### Actors
There are two actors in the workflow described in this NIP:
* Customers (npubs who request a job)
* Service providers (npubs who fulfill jobs)
#### Translation
## 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.
- TODO: nostr.wine HTTP translation
- TODO: event-based 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...
}
```
#### Trending feeds
All tags are optional.
- TODO: primal HTTP trending feeds
- TODO: nostr.band HTTP trending feeds
- TODO: DVM-based 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
#### Whatever
## Encrypted Params
- TODO:
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.

View File

@@ -165,6 +165,9 @@ 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) |
@@ -184,9 +187,11 @@ 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] |
@@ -246,6 +251,8 @@ 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/
@@ -326,6 +333,7 @@ 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) |