Compare commits

...

25 Commits

Author SHA1 Message Date
fiatjaf
1f1ee425b7 draft NIP-54 podcasts NIP. 2024-02-28 12:23:48 -03:00
Alex Gleason
f3a2356484 Merge pull request #1086 from AsaiToshiya/AsaiToshiya-patch-2
README: add q tag
2024-02-25 21:39:10 -06:00
Asai Toshiya
c9b3abfa3c Remove marker
Co-authored-by: Alex Gleason <alex@alexgleason.me>
2024-02-26 12:30:49 +09:00
Asai Toshiya
383eb594b4 README: add q tag 2024-02-26 10:07:55 +09:00
Alex Gleason
4a171cb0a8 Merge pull request #1074 from jb55/quote-repost-q-tags
Use `q` instead of `e` tags for quote reposts
2024-02-25 08:09:09 -06:00
Asai Toshiya
84236acdd9 NIP-92: Clarify that imeta tag can be ignored 2024-02-23 12:26:19 -03:00
William Casarin
996ef45605 Use q instead of e tags for quote reposts
Signed-off-by: William Casarin <jb55@jb55.com>
2024-02-22 11:40:58 -08:00
Asai Toshiya
4313fbf7a5 NIP-46, NIP-49, NIP-65: fix typos 2024-02-22 09:50:12 -03:00
Vitor Pamplona
cbee1092d2 Merge pull request #1063 from alexgleason/nip07-nip44
NIP-07: add NIP-44 calls
2024-02-21 15:11:53 -05:00
Vitor Pamplona
44c678b365 Merge pull request #1065 from coracle-social/no-ck
Remove get_conversation_key
2024-02-21 11:19:45 -05:00
Jon Staab
f1f992e95e Remove get_conversation_key 2024-02-21 08:17:42 -08:00
hodlbod
7995a5bc37 Merge pull request #983 from monlovesmango/NIP46-update-NIP44-calls
NIP46 - update NIP44 calls
2024-02-21 08:11:42 -08:00
monlovesmango
0045106dd1 leave nip44_get_conversation_key undefined for now 2024-02-21 10:03:33 -06:00
monlovesmango
ff39f492e1 update nip44_get_conversation_key 2024-02-20 16:29:01 -06:00
monlovesmango
9c7ae62e53 update nip44 encrypt/decrypt 2024-02-20 16:17:40 -06:00
JeffG
c6cd655c1c New version of NIP-46 (#1047)
* New version of NIP-46

* Update pubkey references

* Document what we have

* Update terms and kind number in discovery

* Update encypt/decrypt calls to handle arrays. Add redirect_uri param for auth_challenges

* Move remote signer commands to own section, add appendix for oauth-like stuff.

* Add diagrams
2024-02-20 17:56:17 -03:00
Asai Toshiya
cbffa7a5de BREAKING.md: update the history (#1055)
* BREAKING.md: add NIP changes until 2023-12-01

* Update BREAKING.md

* BREAKING.md: add NIP changes until 2023-11-01

* Update BREAKING.md

* Remove change to NIP-22 in 2023-12-02

* BREAKING.md: add NIP changes until 2023-08-01

* BREAKING.md: add NIP changes until 2023-04-01

* BREAKING.md: add NIP changes until 2023-03-01

* BREAKING.md: add NIP changes

* Just format
2024-02-20 17:26:44 -03:00
Alex Gleason
2390058a32 NIP-07: remove "and iv" from NIP-44 comments 2024-02-20 12:09:40 -06:00
Alex Gleason
71b0cbc2de NIP-07: add NIP-44 calls 2024-02-20 12:06:06 -06:00
Michael Dilger
36d2281587 NIP-49 test vector for unicode normalization (#1054)
* NIP-49 [breaking]: password normalized to unicode NFKC format

* Test vector for unicode normalization
2024-02-15 23:54:47 -03:00
Michael Dilger
62c48eff58 Breaking changes list (#1052)
* Start of new BREAKING.md changes list

* more

* Make NIP column into links

* notes

* Make commits links

* Remove optional P tag entry, was not breaking

* Update BREAKING with nip-49 breaking change
2024-02-15 22:15:32 -03:00
Mike Dilger
cbec02ab52 NIP-49 [breaking]: password normalized to unicode NFKC format 2024-02-15 21:49:36 -03:00
Semisol
afbb8dd008 remove pgp identity from NIP-39 temporarily
the current scheme recommends signing the *fingerprint* of the pgp key, which doesn't tie into the nostr identity
this commit temporarily undos the commit adding pgp identities until a proper fixed version is merged

just signing the npub string also would not be sufficient, and the message should indicate this is for identity verification

ideally, a link to download key material would also be included
2024-02-15 01:32:38 +03:00
franzap
b5dc891973 Add PGP as NIP-39 external identity 2024-02-14 11:40:16 -03:00
franzap
3f13eb34bf File metadata sets (#1040)
* File metadata sets

* Make kind specific to release artifacts

* Update example

* Update README
2024-02-13 09:40:57 -03:00
10 changed files with 382 additions and 82 deletions

2
07.md
View File

@@ -20,6 +20,8 @@ Aside from these two basic above, the following functions can also be implemente
async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated) async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated) async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip44.encrypt(pubkey, plaintext): string // returns ciphertext as specified in nip-44
async window.nostr.nip44.decrypt(pubkey, ciphertext): string // takes ciphertext as specified in nip-44
``` ```
### Implementation ### Implementation

7
18.md
View File

@@ -20,9 +20,10 @@ reposted.
## Quote Reposts ## Quote Reposts
Quote reposts are `kind 1` events with an embedded `e` tag Quote reposts are `kind 1` events with an embedded `q` tag of the note being
(see [NIP-08](08.md) and [NIP-27](27.md)). Because a quote repost includes quote reposted. The `q` tag ensures quote reposts are not pulled and included
an `e` tag, it may show up along replies to the reposted note. as replies in threads. It also allows you to easily pull and count all of the
quotes for a post.
## Generic Reposts ## Generic Reposts

260
46.md
View File

@@ -1,98 +1,222 @@
NIP-46 # NIP-46 - Nostr Remote Signing
======
Nostr Connect ## Rationale
-------------
`draft` `optional` Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
This NIP describes a method for 2-way communication between a **remote signer** and a normal Nostr client. The remote signer could be, for example, a hardware device dedicated to signing Nostr events, while the client is a normal Nostr client. This NIP describes a method for 2-way communication between a remote signer and a Nostr client. The remote signer could be, for example, a hardware device dedicated to signing Nostr events, while the client is a normal Nostr client.
## Signer Discovery ## Terminology
The client always starts by generating a random key which is used to communicate with the signer, then it one of the methods below is used to allow the client to know what is the signer public key for the session and which relays to use. - **Local keypair**: A local public and private key-pair used to encrypt content and communicate with the remote signer. Usually created by the client application.
- **Remote user pubkey**: The public key that the user wants to sign as. The remote signer has control of the private key that matches this public key.
- **Remote signer pubkey**: This is the public key of the remote signer itself. This is needed in both `create_account` command because you don't yet have a remote user pubkey.
### Started by the signer (nsecBunker) All pubkeys specified in this NIP are in hex format.
The remote signer generates a connection token in the form ## Initiating a connection
To initiate a connection between a client and a remote signer there are a few different options.
### Direct connection initiated by remote signer
This is most common in a situation where you have your own nsecbunker or other type of remote signer and want to connect through a client that supports remote signing.
The remote signer would provide a connection token in the form:
``` ```
bunker://<hex-pubkey>?relay=wss://...&relay=wss://...&secret=<optional-secret> bunker://<remote-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
``` ```
The user copies that token and pastes it in the client UI somehow. Then the client can send events of kind `24133` to the specified relays and wait for responses from the remote signer. This token is pasted into the client by the user and the client then uses the details to connect to the remote signer via the specified relay(s).
### Started by the client ### Direct connection initiated by the client
The client generates a QR code in the following form (URL-encoded): In this case, basically the opposite direction of the first case, the client provides a connection token (or encodes the token in a QR code) and the signer initiates a connection to the client via the specified relay(s).
``` ```
nostrconnect://<client-key-hex>?relay=wss://...&metadata={"name":"...", "url": "...", "description": "..."} nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}>
``` ```
The signer scans the QR code and sends a `connect` message to the client in the specified relays. ## The flow
## Event payloads 1. Client creates a local keypair. This keypair doesn't need to be communicated to the user since it's largely disposable (i.e. the user doesn't need to see this pubkey). Clients might choose to store it locally and they should delete it when the user logs out.
2. Client gets the remote user pubkey (either via a `bunker://` connection string or a NIP-05 login-flow; shown below)
3. Clients use the local keypair to send requests to the remote signer by `p`-tagging and encrypting to the remote user pubkey.
4. The remote signer responds to the client by `p`-tagging and encrypting to the local keypair pubkey.
Event payloads are [NIP-04](04.md)-encrypted JSON blobs that look like JSONRPC messages (their format is specified inside the `.content` of the event formats below). ### Example flow for signing an event
Events sent by the client to the remote signer have the following format: - Remote user pubkey (e.g. signing as) `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
- Local pubkey is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
```js #### Signature request
```json
{ {
"pubkey": "<client-key-hex>" "kind": 24133,
"kind": 24133, "pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
"tags": [ "content": nip04({
["p", "<signer-key-hex>"] "id": <random_string>,
], "method": "sign_event",
"content": "nip04_encrypted_json({id: <random-string>, method: <see-below>, params: [array_of_strings]})", "params": [json_stringified(<{
... content: "Hello, I'm signing remotely",
pubkey: "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
// ...the rest of the event data
}>)]
}),
"tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote user pubkey
} }
``` ```
And the events the remote signer sends to the client have the following format: #### Response event
```js ```json
"pubkey": "<signer-key-hex>" {
"kind": 24133, "kind": 24133,
"tags": [ "pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
["p", "<client-key-hex>"] "content": nip04({
], "id": <random_string>,
"content": "nip04_encrypted_json({id: <request-id>, result: <string>, error: <reason-string>})", "result": json_stringified(<signed-event>)
... }),
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the local keypair pubkey
}
``` ```
The signer key will always be the key of the user who controls the signer device. #### Diagram
### Methods ![signing-example](https://i.nostr.build/P3gW.png)
- **connect** ## Request Events `kind: 24133`
- params: [`pubkey`, `secret`]
- result: `"ack"` ```json
- **get_public_key** {
- params: [] "id": <id>,
- result: `pubkey-hex` "kind": 24133,
- **sign_event** "pubkey": <local_keypair_pubkey>,
- params: [`event`] "content": <nip04(<request>)>,
- result: `json_string(event_with_pubkey_id_and_signature)` "tags": [["p", <remote_user_pubkey>]], // NB: in the `create_account` event, the remote signer pubkey should be `p` tagged.
- **get_relays** "created_at": <unix timestamp in seconds>
- params: [] }
- result: `json_string({[url: string]: {read: boolean, write: boolean}})` ```
- **nip04_encrypt**
- params: [`third-party-pubkey`, `plaintext`] The `content` field is a JSON-RPC-like message that is [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) encrypted and has the following structure:
- result: `nip04-ciphertext`
- **nip04_decrypt** ```json
- params: [`third-party-pubkey`, `nip04-ciphertext`] {
- result: `plaintext` "id": <random_string>,
- **nip44_get_key** "method": <method_name>,
- params: [`third-party-pubkey`] "params": [array_of_strings]
- result: `nip44-conversation-key` }
- **nip44_encrypt** ```
- params: [`third-party-pubkey`, `plaintext`]
- result: `nip44-ciphertext` - `id` is a random string that is a request ID. This same ID will be sent back in the response payload.
- **nip44_decrypt** - `method` is the name of the method/command (detailed below).
- params: [`third-party-pubkey`, `nip44-ciphertext`] - `params` is a positional array of string parameters.
- result: `plaintext`
- **ping** ### Methods/Commands
- params: []
- result: `"pong"` Each of the following are methods that the client sends to the remote signer.
| Command | Params | Result |
| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
| `connect` | `[<remote_user_pubkey>, <optional_secret>]` | "ack" |
| `sign_event` | `[<json_stringified_event_to_sign>]` | `json_stringified(<signed_event>)` |
| `ping` | `[]` | "pong" |
| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
| `get_public_key` | `[]` | `<hex-pubkey>` |
| `nip04_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip04_ciphertext>` |
| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` |
## Response Events `kind:24133`
```json
{
"id": <id>,
"kind": 24133,
"pubkey": <remote_signer_pubkey>,
"content": <nip04(<response>)>,
"tags": [["p", <local_keypair_pubkey>]],
"created_at": <unix timestamp in seconds>
}
```
The `content` field is a JSON-RPC-like message that is [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) encrypted and has the following structure:
```json
{
"id": <request_id>,
"result": <results_string>,
"error": <error_string>
}
```
- `id` is the request ID that this response is for.
- `results` is a string of the result of the call (this can be either a string or a JSON stringified object)
- `error` is an error in string form.
### Auth Challenges
An Auth Challenge is a response that a remote signer can send back when it needs the user to authenticate via other means. This is currently used in the OAuth-like flow enabled by signers like [Nsecbunker](https://github.com/kind-0/nsecbunkerd/). The response `content` object will take the following form:
```json
{
"id": <request_id>,
"result": "auth_url",
"error": <URL_to_display_to_end_user>
}
```
Clients should display (in a popup or new tab) the URL from the `error` field and then subscribe/listen for another response from the remote signer (reusing the same request ID). This event will be sent once the user authenticates in the other window (or will never arrive if the user doesn't authenticate). It's also possible to add a `redirect_uri` url parameter to the auth_url, which is helpful in situations when a client cannot open a new window or tab to display the auth challenge.
#### Example event signing request with auth challenge
![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
## Remote Signer Commands
Remote signers might support additional commands when communicating directly with it. These commands follow the same flow as noted above, the only difference is that when the client sends a request event, the `p`-tag is the pubkey of the remote signer itself and the `content` payload is encrypted to the same remote signer pubkey.
### Methods/Commands
Each of the following are methods that the client sends to the remote signer.
| Command | Params | Result |
| ---------------- | ------------------------------------------ | ------------------------------------ |
| `create_account` | `[<username>, <domain>, <optional_email>]` | `<newly_created_remote_user_pubkey>` |
## Appendix
### NIP-05 Login Flow
Clients might choose to present a more familiar login flow, so users can type a NIP-05 address instead of a `bunker://` string.
When the user types a NIP-05 the client:
- Queries the `/.well-known/nostr.json` file from the domain for the NIP-05 address provided to get the user's pubkey (this is the **remote user pubkey**)
- In the same `/.well-known/nostr.json` file, queries for the `nip46` key to get the relays that the remote signer will be listening on.
- Now the client has enough information to send commands to the remote signer on behalf of the user.
### OAuth-like Flow
#### Remote signer discovery via NIP-89
In this last case, most often used to fascilitate an OAuth-like signin flow, the client first looks for remote signers that have announced themselves via NIP-89 application handler events.
First the client will query for `kind: 31990` events that have a `k` tag of `24133`.
These are generally shown to a user, and once the user selects which remote signer to use and provides the remote user pubkey they want to use (via npub, pubkey, or nip-05 value), the client can initiate a connection. Note that it's on the user to select the remote signer that is actually managing the remote key that they would like to use in this case. If the remote user pubkey is managed on another remote signer, the connection will fail.
In addition, it's important that clients validate that the pubkey of the announced remote signer matches the pubkey of the `_` entry in the `/.well-known/nostr.json` file of the remote signer's announced domain.
Clients that allow users to create new accounts should also consider validating the availability of a given username in the namespace of remote signer's domain by checking the `/.well-known/nostr.json` file for existing usernames. Clients can then show users feedback in the UI before sending a `create_account` event to the remote signer and receiving an error in return. Ideally, remote signers would also respond with understandable error messages if a client tries to create an account with an existing username.
#### Example Oauth-like flow to create a new user account with Nsecbunker
Coming soon...
## References
- [NIP-04 - Encryption](https://github.com/nostr-protocol/nips/blob/master/04.md)

34
49.md
View File

@@ -12,17 +12,17 @@ This NIP defines a method by which clients can encrypt (and decrypt) a user's pr
Symmetric Encryption Key derivation Symmetric Encryption Key derivation
----------------------------------- -----------------------------------
PASSWORD = read from the user PASSWORD = Read from the user. The password should be unicode normalized to NFKC format to ensure that the password can be entered identically on other computers/clients.
LOG\_N = Let the user or implementer choose one byte representing a power of 2 (e.g. 18 represents 262,144) which is used as the number of rounds for scrypt. Larger numbers take more time and more memory, and offer better protection: LOG\_N = Let the user or implementer choose one byte representing a power of 2 (e.g. 18 represents 262,144) which is used as the number of rounds for scrypt. Larger numbers take more time and more memory, and offer better protection:
| LOG\_N | MEMORY REQUIRED | APPROX TIME ON FAST COMPUTER | | LOG_N | MEMORY REQUIRED | APPROX TIME ON FAST COMPUTER |
|--------|-----------------|----------------------------- | |-------|-----------------|----------------------------- |
| 16 | 64 MiB | 100 ms | | 16 | 64 MiB | 100 ms |
| 18 | 256 MiB | | | 18 | 256 MiB | |
| 20 | 1 GiB | 2 seconds | | 20 | 1 GiB | 2 seconds |
| 21 | 2 GiB | | | 21 | 2 GiB | |
| 22 | 4 GiB | | | 22 | 4 GiB | |
SALT = 16 random bytes SALT = 16 random bytes
@@ -78,6 +78,22 @@ The decryption process operates in the reverse.
Test Data Test Data
--------- ---------
## Password Unicode Normalization
The following password input: "ÅΩẛ̣"
- Unicode Codepoints: U+212B U+2126 U+1E9B U+0323
- UTF-8 bytes: [0xE2, 0x84, 0xAB, 0xE2, 0x84, 0xA6, 0xE1, 0xBA, 0x9B, 0xCC, 0xA3]
Should be converted into the unicode normalized NFKC format prior to use in scrypt: "ÅΩẛ̣"
- Unicode Codepoints: U+00C5 U+03A9 U+1E69
- UTF-8 bytes: [0xC3, 0x85, 0xCE, 0xA9, 0xE1, 0xB9, 0xA9]
## Encryption
The encryption process is non-deterministic due to the random nonce.
## Decryption
The following encrypted private key: The following encrypted private key:
`ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p` `ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p`
@@ -86,8 +102,6 @@ When decrypted with password='nostr' and log_n=16 yields the following hex-encod
`3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683` `3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683`
The reverse process is non-deterministic due to the random nonce.
Discussion Discussion
---------- ----------

25
51.md
View File

@@ -49,6 +49,7 @@ Aside from their main identifier, the `"d"` tag, sets can optionally have a `"ti
| Curation sets | 30005 | groups of videos picked by users as interesting and/or belonging to the same category | `"a"` (kind:34235 videos) | | Curation sets | 30005 | groups of videos picked by users as interesting and/or belonging to the same category | `"a"` (kind:34235 videos) |
| Interest sets | 30015 | interest topics represented by a bunch of "hashtags" | `"t"` (hashtags) | | Interest sets | 30015 | interest topics represented by a bunch of "hashtags" | `"t"` (hashtags) |
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) | | Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
| Release artifact sets | 30063 | groups of files of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"i"` (application identifier, typically reverse domain notation), `"version"` |
## Deprecated standard lists ## Deprecated standard lists
@@ -103,6 +104,30 @@ Some clients have used these lists in the past, but they should work on transiti
} }
``` ```
### A _release artifact set_ of an Example App
```json
{
"id": "567b41fc9060c758c4216fe5f8d3df7c57daad7ae757fa4606f0c39d4dd220ef",
"pubkey": "d6dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c",
"created_at": 1695327657,
"kind": 30063,
"tags": [
["d", "ak8dy3v7"],
["i", "com.example.app"],
["version", "0.0.1"],
["title", "Example App"],
["image", "http://cdn.site/p/com.example.app/icon.png"],
["e", "d78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e"], // Windows exe
["e", "f27e2c91051de0c4e1da0d5dce22bfff9db0a9340e0326b4941ed78bae996c9e"], // MacOS dmg
["e", "9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad02332"], // Linux AppImage
["e", "340e0326b340e0326b4941ed78ba340e0326b4941ed78ba340e0326b49ed78ba"] // PWA
],
"content": "Example App is a decentralized marketplace for apps",
"sig": "a9a4e2192eede77e6c9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad001cb039cb8de91d83ce30e9a94f82ac3c5a2372aa1294a96bd"
}
```
## Encryption process pseudocode ## Encryption process pseudocode
```scala ```scala

80
54.md Normal file
View File

@@ -0,0 +1,80 @@
NIP-54
======
Podcasts
--------
`draft` `optional`
This NIP defines how podcast episodes can be fetched from relays. It's intended to fit easily into existing podcast players.
## Rationale
RSS feeds are great, but they have some problems that are solved by moving feeds from RSS URLs to multiple Nostr events, one for each episode:
- they depend on a URL and that is hard for most people, so podcasters tend to use service providers that can
- charge money -- which isn't a bad thing per se but it turns out that with a Nostr-native podcast feed protocol it would be much simpler to host feeds and infinitely cheaper to host the media, so the ecosystem could be more decentralized
- seal them into their walled gardens (like Spotify has been doing), slowly turning a previously open ecosystem into a centralized system captured by big corporations
- censor podcasters and prevent them from migrating (on Nostr this wouldn't be a problem even if a relay banned someone as moving to other relays would be trivial as long as the podcaster held their key and podcast clients could easily discover the new relay URLs)
- they can only be loaded in full, with no pagination or filtering of any kind, which means that
- the sync process in normal RSS clients is slow and cumbersome (which also nudges people into using centralized solutions)
- this has also led to the creation of broken schemes like [Podping](https://podping.org/), which wouldn't be necessary in a Nostr-native podcast feed
- It's impossible to reference a single episode directly (since it only exists as a member in the full RSS list of episodes), this means there is no way to share a podcast episode with friends or on social media, so people tend to share links to centralized platforms or to YouTube videos of the same podcast content, which is an antipattern
- they cannot be interacted with in any way: listeners cannot "like" or comment or signal that they have listened to the episode, which is also another factor in the push towards centralized closed platforms: better analytics and insights and possibilities of engagement with the public
## Event definitions
### Podcast Profile
Podcasts have their own key and their own [NIP-01](01.md) `kind:0` profile, but with a tag `["type", "podcast"]` that can be used to signal that they have podcast episodes published.
### Podcast Episodes
Podcast episodes are `kind:54` with some tags:
```jsonc
{
"id": "55807e7d5cd90d0303d7dce7397f996fdbaed8697903f326c7cf8ad999b9de3d",
"pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"kind": 54,
"created_at": 1700682555,
"tags": [
["title", "<episode title>"],
["image", "<optional episode image>"],
["description", "<a brief description>"],
["audio", "https://.../", "<optional_media_type>"], // can be specified multiple times
["t", "<optional tag>"], // can be specified multiple times
["alt", "<optional NIP-31 short description for displaying in incompatible clients>"]
],
"content": "<markdown content (or what is supported in RSS feeds content nowadays?)>",
"sig": "...",
}
```
### Podcast Comments
These are normal text comments like `kind:1`, but specific for podcast episodes.
```jsonc
{
"id": "606f6c703f04c70806a5c2830e3853bd83c44985b1f282a070ba894add3724b0",
"pubkey": "b2c5d5bf69d14d35c25fa47d5c67896b13394136734065a371c5bf2c62f905d2",
"kind": 55,
"created_at": 1700682556,
"tags": [
["e", "55807e7d5cd90d0303d7dce7397f996fdbaed8697903f326c7cf8ad999b9de3d"],
],
"content": "<plain text content>",
"sig": "...",
}
```
(It's unclear if it's best to use this or a generic kind for commenting on stuff that isn't `kind:1`.)
### Reactions (likes), zaps and reposts
These work normally according to their own NIPs.
### To be added:
- "read" events, signaling that a user has listened to a given podcast, perhaps just use https://github.com/nostr-protocol/nips/pull/933

2
65.md
View File

@@ -19,7 +19,7 @@ The `.content` is not used.
["r", "wss://alicerelay.example.com"], ["r", "wss://alicerelay.example.com"],
["r", "wss://brando-relay.com"], ["r", "wss://brando-relay.com"],
["r", "wss://expensive-relay.example2.com", "write"], ["r", "wss://expensive-relay.example2.com", "write"],
["r", "wss://nostr-relay.example.com", "read"], ["r", "wss://nostr-relay.example.com", "read"]
], ],
"content": "", "content": "",
...other fields ...other fields

2
92.md
View File

@@ -41,3 +41,5 @@ after the file is uploaded and included in the post.
When pasting URLs during post composition, the client MAY download the file When pasting URLs during post composition, the client MAY download the file
and add this metadata before the post is sent. and add this metadata before the post is sent.
The client MAY ignore `imeta` tags that do not match the URL in the event content.

45
BREAKING.md Normal file
View File

@@ -0,0 +1,45 @@
# Breaking Changes
This is a history of NIP changes that potentially break pre-existing implementations, in
reverse chronological order.
| Date | Commit | NIP | Change |
| ----------- | --------- | -------- | ------ |
| 2024-02-16 | [cbec02ab](https://github.com/nostr-protocol/nips/commit/cbec02ab) | [NIP-49](49.md) | Password first normalized to NFKC |
| 2024-02-15 | [afbb8dd0](https://github.com/nostr-protocol/nips/commit/afbb8dd0) | [NIP-39](39.md) | PGP identity was removed |
| 2024-02-07 | [d3dad114](https://github.com/nostr-protocol/nips/commit/d3dad114) | [NIP-46](46.md) | Connection token format was changed |
| 2024-01-30 | [1a2b21b6](https://github.com/nostr-protocol/nips/commit/1a2b21b6) | [NIP-59](59.md) | 'p' tag became optional |
| 2023-01-27 | [c2f34817](https://github.com/nostr-protocol/nips/commit/c2f34817) | [NIP-47](47.md) | optional expiration tag should be honored |
| 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [NIP-02](02.md) | list entries should be chronological |
| 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [NIP-51](51.md) | list entries should be chronological |
| 2023-12-30 | [29869821](https://github.com/nostr-protocol/nips/commit/29869821) | [NIP-52](52.md) | 'name' tag was removed (use 'title' tag instead) |
| 2023-12-27 | [17c67ef5](https://github.com/nostr-protocol/nips/commit/17c67ef5) | [NIP-94](94.md) | 'aes-256-gcm' tag was removed |
| 2023-12-03 | [0ba45895](https://github.com/nostr-protocol/nips/commit/0ba45895) | [NIP-01](01.md) | WebSocket status code `4000` was replaced by 'CLOSED' message |
| 2023-11-28 | [6de35f9e](https://github.com/nostr-protocol/nips/commit/6de35f9e) | [NIP-89](89.md) | 'client' tag value was changed |
| 2023-11-20 | [7822a8b1](https://github.com/nostr-protocol/nips/commit/7822a8b1) | [NIP-51](51.md) | `kind: 30000` and `kind: 30001` were deprecated |
| 2023-11-11 | [cbdca1e9](https://github.com/nostr-protocol/nips/commit/cbdca1e9) | [NIP-84](84.md) | 'range' tag was removed |
| 2023-11-07 | [108b7f16](https://github.com/nostr-protocol/nips/commit/108b7f16) | [NIP-01](01.md) | 'OK' message must have 4 items |
| 2023-10-17 | [cf672b76](https://github.com/nostr-protocol/nips/commit/cf672b76) | [NIP-03](03.md) | 'block' tag was removed |
| 2023-09-29 | [7dc6385f](https://github.com/nostr-protocol/nips/commit/7dc6385f) | [NIP-57](57.md) | optional 'a' tag was included in `zap receipt` |
| 2023-08-21 | [89915e02](https://github.com/nostr-protocol/nips/commit/89915e02) | [NIP-11](11.md) | 'min_prefix' was removed |
| 2023-08-20 | [37c4375e](https://github.com/nostr-protocol/nips/commit/37c4375e) | [NIP-01](01.md) | replaceable events with same timestamp should be retained event with lowest id |
| 2023-08-15 | [88ee873c](https://github.com/nostr-protocol/nips/commit/88ee873c) | [NIP-15](15.md) | 'countries' tag was renamed to 'regions' |
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-12](12.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-16](16.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-20](20.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-33](33.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
| 2023-08-11 | [d87f8617](https://github.com/nostr-protocol/nips/commit/d87f8617) | [NIP-25](25.md) | empty `content` should be considered as "+" |
| 2023-08-01 | [5d63b157](https://github.com/nostr-protocol/nips/commit/5d63b157) | [NIP-57](57.md) | 'zap' tag was changed |
| 2023-07-15 | [d1814405](https://github.com/nostr-protocol/nips/commit/d1814405) | [NIP-01](01.md) | `since` and `until` filters should be `since <= created_at <= until` |
| 2023-07-12 | [a1cd2bd8](https://github.com/nostr-protocol/nips/commit/a1cd2bd8) | [NIP-25](25.md) | custom emoji was supported |
| 2023-06-18 | [83cbd3e1](https://github.com/nostr-protocol/nips/commit/83cbd3e1) | [NIP-11](11.md) | 'image' was renamed to 'icon' |
| 2023-04-13 | [bf0a0da6](https://github.com/nostr-protocol/nips/commit/bf0a0da6) | [NIP-15](15.md) | different NIP was re-added as NIP-15 |
| 2023-04-09 | [fb5b7c73](https://github.com/nostr-protocol/nips/commit/fb5b7c73) | [NIP-15](15.md) | NIP-15 was merged into NIP-01 |
| 2023-03-15 | [e1004d3d](https://github.com/nostr-protocol/nips/commit/e1004d3d) | [NIP-19](19.md) | `1: relay` was changed to optionally |
Breaking changes prior to 2023-03-01 are not yet documented.
## NOTES
- If it isn't clear that a change is breaking or not, we list it.
- The date is the date it was merged, not necessarily the date of the commit.

View File

@@ -15,6 +15,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [Criteria for acceptance of NIPs](#criteria-for-acceptance-of-nips) - [Criteria for acceptance of NIPs](#criteria-for-acceptance-of-nips)
- [Is this repository a centralizing factor?](#is-this-repository-a-centralizing-factor) - [Is this repository a centralizing factor?](#is-this-repository-a-centralizing-factor)
- [How this repository works](#how-this-repository-works) - [How this repository works](#how-this-repository-works)
- [Breaking Changes](#breaking-changes)
- [License](#license) - [License](#license)
--- ---
@@ -148,6 +149,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `30023` | Long-form Content | [23](23.md) | | `30023` | Long-form Content | [23](23.md) |
| `30024` | Draft Long-form Content | [23](23.md) | | `30024` | Draft Long-form Content | [23](23.md) |
| `30030` | Emoji sets | [51](51.md) | | `30030` | Emoji sets | [51](51.md) |
| `30063` | Release artifact sets | [51](51.md) |
| `30078` | Application-specific Data | [78](78.md) | | `30078` | Application-specific Data | [78](78.md) |
| `30311` | Live Event | [53](53.md) | | `30311` | Live Event | [53](53.md) |
| `30315` | User Statuses | [38](38.md) | | `30315` | User Statuses | [38](38.md) |
@@ -204,6 +206,7 @@ Please update these lists when proposing NIPs introducing new event kinds.
| `l` | label, label namespace | annotations | [32](32.md) | | `l` | label, label namespace | annotations | [32](32.md) |
| `L` | label namespace | -- | [32](32.md) | | `L` | label namespace | -- | [32](32.md) |
| `m` | MIME type | -- | [94](94.md) | | `m` | MIME type | -- | [94](94.md) |
| `q` | event id (hex) | relay URL | [18](18.md) |
| `r` | a reference (URL, etc) | petname | | | `r` | a reference (URL, etc) | petname | |
| `r` | relay url | marker | [65](65.md) | | `r` | relay url | marker | [65](65.md) |
| `t` | hashtag | -- | | | `t` | hashtag | -- | |
@@ -260,6 +263,10 @@ Standards may emerge in two ways: the first way is that someone starts doing som
These two ways of standardizing things are supported by this repository. Although the second is preferred, an effort will be made to codify standards emerged outside this repository into NIPs that can be later referenced and easily understood and implemented by others -- but obviously as in any human system discretion may be applied when standards are considered harmful. These two ways of standardizing things are supported by this repository. Although the second is preferred, an effort will be made to codify standards emerged outside this repository into NIPs that can be later referenced and easily understood and implemented by others -- but obviously as in any human system discretion may be applied when standards are considered harmful.
## Breaking Changes
[Breaking Changes](BREAKING.md)
## License ## License
All NIPs are public domain. All NIPs are public domain.