diff options
| -rw-r--r-- | 01.md | 2 | ||||
| -rw-r--r-- | 02.md | 4 | ||||
| -rw-r--r-- | 10.md | 4 | ||||
| -rw-r--r-- | 13.md | 2 | ||||
| -rw-r--r-- | 25.md | 3 | ||||
| -rw-r--r-- | 32.md | 2 | ||||
| -rw-r--r-- | 35.md | 70 | ||||
| -rw-r--r-- | 38.md | 4 | ||||
| -rw-r--r-- | 53.md | 2 | ||||
| -rw-r--r-- | 54.md | 5 | ||||
| -rw-r--r-- | 55.md | 538 | ||||
| -rw-r--r-- | 71.md | 2 | ||||
| -rw-r--r-- | BREAKING.md | 1 | ||||
| -rw-r--r-- | README.md | 110 |
14 files changed, 686 insertions, 63 deletions
| @@ -87,7 +87,7 @@ As a convention, all single-letter (only english alphabet letters: a-z, A-Z) key | |||
| 87 | 87 | ||
| 88 | Kinds specify how clients should interpret the meaning of each event and the other fields of each event (e.g. an `"r"` tag may have a meaning in an event of kind 1 and an entirely different meaning in an event of kind 10002). Each NIP may define the meaning of a set of kinds that weren't defined elsewhere. This NIP defines two basic kinds: | 88 | Kinds specify how clients should interpret the meaning of each event and the other fields of each event (e.g. an `"r"` tag may have a meaning in an event of kind 1 and an entirely different meaning in an event of kind 10002). Each NIP may define the meaning of a set of kinds that weren't defined elsewhere. This NIP defines two basic kinds: |
| 89 | 89 | ||
| 90 | - `0`: **metadata**: the `content` is set to a stringified JSON object `{name: <username>, about: <string>, picture: <url, string>}` describing the user who created the event. A relay may delete older events once it gets a new one for the same pubkey. | 90 | - `0`: **metadata**: the `content` is set to a stringified JSON object `{name: <username>, about: <string>, picture: <url, string>}` describing the user who created the event. [Extra metadata fields](24.md#kind-0) may be set. A relay may delete older events once it gets a new one for the same pubkey. |
| 91 | - `1`: **text note**: the `content` is set to the **plaintext** content of a note (anything the user wants to say). Content that must be parsed, such as Markdown and HTML, should not be used. Clients should also not parse content as those. | 91 | - `1`: **text note**: the `content` is set to the **plaintext** content of a note (anything the user wants to say). Content that must be parsed, such as Markdown and HTML, should not be used. Clients should also not parse content as those. |
| 92 | 92 | ||
| 93 | And also a convention for kind ranges that allow for easier experimentation and flexibility of relay implementation: | 93 | And also a convention for kind ranges that allow for easier experimentation and flexibility of relay implementation: |
| @@ -8,7 +8,9 @@ Follow List | |||
| 8 | 8 | ||
| 9 | A special event with kind `3`, meaning "follow list" is defined as having a list of `p` tags, one for each of the followed/known profiles one is following. | 9 | A special event with kind `3`, meaning "follow list" is defined as having a list of `p` tags, one for each of the followed/known profiles one is following. |
| 10 | 10 | ||
| 11 | Each tag entry should contain the key for the profile, a relay URL where events from that key can be found (can be set to an empty string if not needed), and a local name (or "petname") for that profile (can also be set to an empty string or not provided), i.e., `["p", <32-bytes hex key>, <main relay URL>, <petname>]`. The `content` can be anything and should be ignored. | 11 | Each tag entry should contain the key for the profile, a relay URL where events from that key can be found (can be set to an empty string if not needed), and a local name (or "petname") for that profile (can also be set to an empty string or not provided), i.e., `["p", <32-bytes hex key>, <main relay URL>, <petname>]`. |
| 12 | |||
| 13 | The `.content` is not used. | ||
| 12 | 14 | ||
| 13 | For example: | 15 | For example: |
| 14 | 16 | ||
| @@ -38,13 +38,14 @@ They are citing from this event. `root-id` and `reply-id` are as above. | |||
| 38 | >This scheme is deprecated because it creates ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply. | 38 | >This scheme is deprecated because it creates ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply. |
| 39 | 39 | ||
| 40 | ## Marked "e" tags (PREFERRED) | 40 | ## Marked "e" tags (PREFERRED) |
| 41 | `["e", <event-id>, <relay-url>, <marker>]` | 41 | `["e", <event-id>, <relay-url>, <marker>, <pubkey>]` |
| 42 | 42 | ||
| 43 | Where: | 43 | Where: |
| 44 | 44 | ||
| 45 | * `<event-id>` is the id of the event being referenced. | 45 | * `<event-id>` is the id of the event being referenced. |
| 46 | * `<relay-url>` is the URL of a recommended relay associated with the reference. Clients SHOULD add a valid `<relay-URL>` field, but may instead leave it as `""`. | 46 | * `<relay-url>` is the URL of a recommended relay associated with the reference. Clients SHOULD add a valid `<relay-URL>` field, but may instead leave it as `""`. |
| 47 | * `<marker>` is optional and if present is one of `"reply"`, `"root"`, or `"mention"`. | 47 | * `<marker>` is optional and if present is one of `"reply"`, `"root"`, or `"mention"`. |
| 48 | * `<pubkey>` is optional, SHOULD be the pubkey of the author of the referenced event | ||
| 48 | 49 | ||
| 49 | Those marked with `"reply"` denote the id of the reply event being responded to. Those marked with `"root"` denote the root id of the reply thread being responded to. For top level replies (those replying directly to the root event), only the `"root"` marker should be used. Those marked with `"mention"` denote a quoted or reposted event id. | 50 | Those marked with `"reply"` denote the id of the reply event being responded to. Those marked with `"root"` denote the root id of the reply thread being responded to. For top level replies (those replying directly to the root event), only the `"root"` marker should be used. Those marked with `"mention"` denote a quoted or reposted event id. |
| 50 | 51 | ||
| @@ -52,6 +53,7 @@ A direct reply to the root of a thread should have a single marked "e" tag of ty | |||
| 52 | 53 | ||
| 53 | >This scheme is preferred because it allows events to mention others without confusing them with `<reply-id>` or `<root-id>`. | 54 | >This scheme is preferred because it allows events to mention others without confusing them with `<reply-id>` or `<root-id>`. |
| 54 | 55 | ||
| 56 | `<pubkey>` SHOULD be the pubkey of the author of the `e` tagged event, this is used in the outbox model to search for that event from the authors write relays where relay hints did not resolve the event. | ||
| 55 | 57 | ||
| 56 | ## The "p" tag | 58 | ## The "p" tag |
| 57 | Used in a text event contains a list of pubkeys used to record who is involved in a reply thread. | 59 | Used in a text event contains a list of pubkeys used to record who is involved in a reply thread. |
| @@ -35,7 +35,7 @@ Example mined note | |||
| 35 | "created_at": 1651794653, | 35 | "created_at": 1651794653, |
| 36 | "kind": 1, | 36 | "kind": 1, |
| 37 | "tags": [ | 37 | "tags": [ |
| 38 | ["nonce", "776797", "21"] | 38 | ["nonce", "776797", "20"] |
| 39 | ], | 39 | ], |
| 40 | "content": "It's just me mining my own business", | 40 | "content": "It's just me mining my own business", |
| 41 | "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" | 41 | "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" |
| @@ -67,8 +67,7 @@ content as an emoji if shortcode is specified. | |||
| 67 | "tags": [ | 67 | "tags": [ |
| 68 | ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"] | 68 | ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"] |
| 69 | ], | 69 | ], |
| 70 | "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6", | 70 | ...other fields |
| 71 | "created_at": 1682790000 | ||
| 72 | } | 71 | } |
| 73 | ``` | 72 | ``` |
| 74 | 73 | ||
| @@ -158,4 +158,4 @@ Appendix: Known Ontologies | |||
| 158 | 158 | ||
| 159 | Below is a non-exhaustive list of ontologies currently in widespread use. | 159 | Below is a non-exhaustive list of ontologies currently in widespread use. |
| 160 | 160 | ||
| 161 | - (social.ontolo.categories)[https://ontolo.social/] | 161 | - [social.ontolo.categories](https://ontolo.social/) |
| @@ -0,0 +1,70 @@ | |||
| 1 | NIP-35 | ||
| 2 | ====== | ||
| 3 | |||
| 4 | Torrents | ||
| 5 | ----------- | ||
| 6 | |||
| 7 | `draft` `optional` | ||
| 8 | |||
| 9 | This NIP defined a new `kind 2003` which is a Torrent. | ||
| 10 | |||
| 11 | `kind 2003` is a simple torrent index where there is enough information to search for content and construct the magnet link. No torrent files exist on nostr. | ||
| 12 | |||
| 13 | ## Tags | ||
| 14 | - `x`: V1 BitTorrent Info Hash, as seen in the [magnet link](https://www.bittorrent.org/beps/bep_0053.html) `magnet:?xt=urn:btih:HASH` | ||
| 15 | - `file`: A file entry inside the torrent, including the full path ie. `info/example.txt` | ||
| 16 | - `tracker`: (Optional) A tracker to use for this torrent | ||
| 17 | |||
| 18 | In order to make torrents searchable by general category, you SHOULD include a few tags like `movie`, `tv`, `HD`, `UHD` etc. | ||
| 19 | |||
| 20 | ## Tag prefixes | ||
| 21 | |||
| 22 | Tag prefixes are used to label the content with references, ie. `["i", "imdb:1234"]` | ||
| 23 | |||
| 24 | - `tcat`: A comma separated text category path, ie. `["i", "tcat:video,movie,4k"]`, this should also match the `newznab` category in a best effort approach. | ||
| 25 | - `newznab`: The category ID from [newznab](https://github.com/Prowlarr/Prowlarr/blob/develop/src/NzbDrone.Core/Indexers/NewznabStandardCategory.cs) | ||
| 26 | - `tmdb`: [The movie database](https://www.themoviedb.org/) id. | ||
| 27 | - `ttvdb`: [TV database](https://thetvdb.com/) id. | ||
| 28 | - `imdb`: [IMDB](https://www.imdb.com/) id. | ||
| 29 | - `mal`: [MyAnimeList](https://myanimelist.net/) id. | ||
| 30 | - `anilist`: [AniList](https://anilist.co/) id. | ||
| 31 | |||
| 32 | A second level prefix should be included where the database supports multiple media types. | ||
| 33 | - `tmdb:movie:693134` maps to `themoviedb.org/movie/693134` | ||
| 34 | - `ttvdb:movie:290272` maps to `thetvdb.com/movies/dune-part-two` | ||
| 35 | - `mal:anime:9253` maps to `myanimelist.net/anime/9253` | ||
| 36 | - `mal:manga:17517` maps to `myanimelist.net/manga/17517` | ||
| 37 | |||
| 38 | In some cases the url mapping isnt direct, mapping the url in general is out of scope for this NIP, the section above is only a guide so that implementers have enough information to succsesfully map the url if they wish. | ||
| 39 | |||
| 40 | ```jsonc | ||
| 41 | { | ||
| 42 | "kind": 2003, | ||
| 43 | "content": "<long-description-pre-formatted>", | ||
| 44 | "tags": [ | ||
| 45 | ["title", "<torrent-title>"], | ||
| 46 | ["x", "<bittorrent-info-hash>"], | ||
| 47 | ["file", "<file-name>", "<file-size-in-bytes>"], | ||
| 48 | ["file", "<file-name>", "<file-size-in-bytes>"], | ||
| 49 | ["tracker", "udp://mytacker.com:1337"], | ||
| 50 | ["tracker", "http://1337-tracker.net/announce"], | ||
| 51 | ["i", "tcat:video,movie,4k"], | ||
| 52 | ["i", "newznab:2045"], | ||
| 53 | ["i", "imdb:tt15239678"], | ||
| 54 | ["i", "tmdb:movie:693134"], | ||
| 55 | ["i", "ttvdb:movie:290272"], | ||
| 56 | ["t", "movie"], | ||
| 57 | ["t", "4k"], | ||
| 58 | ] | ||
| 59 | } | ||
| 60 | ``` | ||
| 61 | |||
| 62 | ## Torrent Comments | ||
| 63 | |||
| 64 | A torrent comment is a `kind 2004` event which is used to reply to a torrent event. | ||
| 65 | |||
| 66 | This event works exactly like a `kind 1` and should follow `NIP-10` for tagging. | ||
| 67 | |||
| 68 | ## Implementations | ||
| 69 | 1. [dtan.xyz](https://git.v0l.io/Kieran/dtan) | ||
| 70 | 2. [nostrudel.ninja](https://github.com/hzrd149/nostrudel/tree/next/src/views/torrents) \ No newline at end of file | ||
| @@ -46,6 +46,8 @@ Any other status types can be used but they are not defined by this NIP. | |||
| 46 | 46 | ||
| 47 | The status MAY include an `r`, `p`, `e` or `a` tag linking to a URL, profile, note, or parameterized replaceable event. | 47 | The status MAY include an `r`, `p`, `e` or `a` tag linking to a URL, profile, note, or parameterized replaceable event. |
| 48 | 48 | ||
| 49 | The `content` MAY include emoji(s), or [NIP-30](30.md) custom emoji(s). If the `content` is an empty string then the client should clear the status. | ||
| 50 | |||
| 49 | # Client behavior | 51 | # Client behavior |
| 50 | 52 | ||
| 51 | Clients MAY display this next to the username on posts or profiles to provide live user status information. | 53 | Clients MAY display this next to the username on posts or profiles to provide live user status information. |
| @@ -57,5 +59,3 @@ Clients MAY display this next to the username on posts or profiles to provide li | |||
| 57 | * Nostr music streaming services that update your music status when you're listening | 59 | * Nostr music streaming services that update your music status when you're listening |
| 58 | * Podcasting apps that update your music status when you're listening to a podcast, with a link for others to listen as well | 60 | * Podcasting apps that update your music status when you're listening to a podcast, with a link for others to listen as well |
| 59 | * Clients can use the system media player to update playing music status | 61 | * Clients can use the system media player to update playing music status |
| 60 | |||
| 61 | The `content` MAY include emoji(s), or [NIP-30](30.md) custom emoji(s). If the `content` is an empty string then the client should clear the status. | ||
| @@ -77,7 +77,7 @@ Event `kind:1311` is live chat's channel message. Clients MUST include the `a` t | |||
| 77 | 77 | ||
| 78 | ## Use Cases | 78 | ## Use Cases |
| 79 | 79 | ||
| 80 | Common use cases include meeting rooms/workshops, watch-together activities, or event spaces, such as [live.snort.social](https://live.snort.social) and [nostrnests.com](https://nostrnests.com). | 80 | Common use cases include meeting rooms/workshops, watch-together activities, or event spaces, such as [zap.stream](https://zap.stream). |
| 81 | 81 | ||
| 82 | ## Example | 82 | ## Example |
| 83 | 83 | ||
| @@ -36,6 +36,11 @@ The content should be Markdown, following the same rules as of [NIP-23](23.md), | |||
| 36 | 36 | ||
| 37 | One extra functionality is added: **wikilinks**. Unlike normal Markdown links `[]()` that link to webpages, wikilinks `[[]]` link to other articles in the wiki. In this case, the wiki is the entirety of Nostr. Clicking on a wikilink should cause the client to ask relays for events with `d` tags equal to the target of that wikilink. | 37 | One extra functionality is added: **wikilinks**. Unlike normal Markdown links `[]()` that link to webpages, wikilinks `[[]]` link to other articles in the wiki. In this case, the wiki is the entirety of Nostr. Clicking on a wikilink should cause the client to ask relays for events with `d` tags equal to the target of that wikilink. |
| 38 | 38 | ||
| 39 | Wikilinks can take these two forms: | ||
| 40 | |||
| 41 | 1. `[[Target Page]]` -- in this case it will link to the page `target-page` (according to `d` tag normalization rules above) and be displayed as `Target Page`; | ||
| 42 | 2. `[[target page|see this]]` -- in this case it will link to the page `target-page`, but will be displayed as `see this`. | ||
| 43 | |||
| 39 | ### Merge Requests | 44 | ### Merge Requests |
| 40 | 45 | ||
| 41 | Event `kind:818` represents a request to merge from a forked article into the source. It is directed to a pubkey and references the original article and the modified event. | 46 | Event `kind:818` represents a request to merge from a forked article into the source. It is directed to a pubkey and references the original article and the modified event. |
| @@ -0,0 +1,538 @@ | |||
| 1 | # NIP-55 | ||
| 2 | |||
| 3 | ## Android Signer Application | ||
| 4 | |||
| 5 | `draft` `optional` | ||
| 6 | |||
| 7 | This NIP describes a method for 2-way communication between an Android signer and any Nostr client on Android. The Android signer is an Android Application and the client can be a web client or an Android application. | ||
| 8 | |||
| 9 | # Usage for Android applications | ||
| 10 | |||
| 11 | The Android signer uses Intents and Content Resolvers to communicate between applications. | ||
| 12 | |||
| 13 | To be able to use the Android signer in your application you should add this to your AndroidManifest.xml: | ||
| 14 | |||
| 15 | ```xml | ||
| 16 | <queries> | ||
| 17 | <intent> | ||
| 18 | <action android:name="android.intent.action.VIEW" /> | ||
| 19 | <category android:name="android.intent.category.BROWSABLE" /> | ||
| 20 | <data android:scheme="nostrsigner" /> | ||
| 21 | </intent> | ||
| 22 | </queries> | ||
| 23 | ``` | ||
| 24 | |||
| 25 | Then you can use this function to check if there's a signer application installed: | ||
| 26 | |||
| 27 | ```kotlin | ||
| 28 | fun isExternalSignerInstalled(context: Context): Boolean { | ||
| 29 | val intent = | ||
| 30 | Intent().apply { | ||
| 31 | action = Intent.ACTION_VIEW | ||
| 32 | data = Uri.parse("nostrsigner:") | ||
| 33 | } | ||
| 34 | val infos = context.packageManager.queryIntentActivities(intent, 0) | ||
| 35 | return infos.size > 0 | ||
| 36 | } | ||
| 37 | ``` | ||
| 38 | |||
| 39 | ## Using Intents | ||
| 40 | |||
| 41 | To get the result back from the Signer Application you should use `registerForActivityResult` or `rememberLauncherForActivityResult` in Kotlin. If you are using another framework check the documentation of your framework or a third party library to get the result. | ||
| 42 | |||
| 43 | ```kotlin | ||
| 44 | val launcher = rememberLauncherForActivityResult( | ||
| 45 | contract = ActivityResultContracts.StartActivityForResult(), | ||
| 46 | onResult = { result -> | ||
| 47 | if (result.resultCode != Activity.RESULT_OK) { | ||
| 48 | Toast.makeText( | ||
| 49 | context, | ||
| 50 | "Sign request rejected", | ||
| 51 | Toast.LENGTH_SHORT | ||
| 52 | ).show() | ||
| 53 | } else { | ||
| 54 | val signature = activityResult.data?.getStringExtra("signature") | ||
| 55 | // Do something with signature ... | ||
| 56 | } | ||
| 57 | } | ||
| 58 | ) | ||
| 59 | ``` | ||
| 60 | |||
| 61 | Create the Intent using the **nostrsigner** scheme: | ||
| 62 | |||
| 63 | ```kotlin | ||
| 64 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$content")) | ||
| 65 | ``` | ||
| 66 | |||
| 67 | Set the Signer package name: | ||
| 68 | |||
| 69 | ```kotlin | ||
| 70 | intent.`package` = "com.example.signer" | ||
| 71 | ``` | ||
| 72 | |||
| 73 | Send the Intent: | ||
| 74 | |||
| 75 | ```kotlin | ||
| 76 | launcher.launch(intent) | ||
| 77 | ``` | ||
| 78 | |||
| 79 | ### Methods | ||
| 80 | |||
| 81 | - **get_public_key** | ||
| 82 | - params: | ||
| 83 | |||
| 84 | ```kotlin | ||
| 85 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:")) | ||
| 86 | intent.`package` = "com.example.signer" | ||
| 87 | intent.putExtra("type", "get_public_key") | ||
| 88 | // You can send some default permissions for the user to authorize for ever | ||
| 89 | val permissions = listOf( | ||
| 90 | Permission( | ||
| 91 | type = "sign_event", | ||
| 92 | kind = 22242 | ||
| 93 | ), | ||
| 94 | Permission( | ||
| 95 | type = "nip44_decrypt" | ||
| 96 | ) | ||
| 97 | ) | ||
| 98 | intent.putExtra("permissions", permissions.toJson()) | ||
| 99 | context.startActivity(intent) | ||
| 100 | ``` | ||
| 101 | - result: | ||
| 102 | - If the user approved intent it will return the **npub** in the signature field | ||
| 103 | |||
| 104 | ```kotlin | ||
| 105 | val npub = intent.data?.getStringExtra("signature") | ||
| 106 | // The package name of the signer application | ||
| 107 | val packageName = intent.data?.getStringExtra("package") | ||
| 108 | ``` | ||
| 109 | |||
| 110 | - **sign_event** | ||
| 111 | - params: | ||
| 112 | |||
| 113 | ```kotlin | ||
| 114 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$eventJson")) | ||
| 115 | intent.`package` = "com.example.signer" | ||
| 116 | intent.putExtra("type", "sign_event") | ||
| 117 | // To handle results when not waiting between intents | ||
| 118 | intent.putExtra("id", event.id) | ||
| 119 | // Send the current logged in user npub | ||
| 120 | intent.putExtra("current_user", npub) | ||
| 121 | |||
| 122 | context.startActivity(intent) | ||
| 123 | ``` | ||
| 124 | - result: | ||
| 125 | - If the user approved intent it will return the **signature**, **id** and **event** fields | ||
| 126 | |||
| 127 | ```kotlin | ||
| 128 | val signature = intent.data?.getStringExtra("signature") | ||
| 129 | // The id you sent | ||
| 130 | val id = intent.data?.getStringExtra("id") | ||
| 131 | val signedEventJson = intent.data?.getStringExtra("event") | ||
| 132 | ``` | ||
| 133 | |||
| 134 | - **nip04_encrypt** | ||
| 135 | - params: | ||
| 136 | |||
| 137 | ```kotlin | ||
| 138 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$plaintext")) | ||
| 139 | intent.`package` = "com.example.signer" | ||
| 140 | intent.putExtra("type", "nip04_encrypt") | ||
| 141 | // to control the result in your application in case you are not waiting the result before sending another intent | ||
| 142 | intent.putExtra("id", "some_id") | ||
| 143 | // Send the current logged in user npub | ||
| 144 | intent.putExtra("current_user", account.keyPair.pubKey.toNpub()) | ||
| 145 | // Send the hex pubKey that will be used for encrypting the data | ||
| 146 | intent.putExtra("pubKey", pubKey) | ||
| 147 | |||
| 148 | context.startActivity(intent) | ||
| 149 | ``` | ||
| 150 | - result: | ||
| 151 | - If the user approved intent it will return the **signature** and **id** fields | ||
| 152 | |||
| 153 | ```kotlin | ||
| 154 | val encryptedText = intent.data?.getStringExtra("signature") | ||
| 155 | // the id you sent | ||
| 156 | val id = intent.data?.getStringExtra("id") | ||
| 157 | ``` | ||
| 158 | |||
| 159 | - **nip44_encrypt** | ||
| 160 | - params: | ||
| 161 | |||
| 162 | ```kotlin | ||
| 163 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$plaintext")) | ||
| 164 | intent.`package` = "com.example.signer" | ||
| 165 | intent.putExtra("type", "nip44_encrypt") | ||
| 166 | // to control the result in your application in case you are not waiting the result before sending another intent | ||
| 167 | intent.putExtra("id", "some_id") | ||
| 168 | // Send the current logged in user npub | ||
| 169 | intent.putExtra("current_user", account.keyPair.pubKey.toNpub()) | ||
| 170 | // Send the hex pubKey that will be used for encrypting the data | ||
| 171 | intent.putExtra("pubKey", pubKey) | ||
| 172 | |||
| 173 | context.startActivity(intent) | ||
| 174 | ``` | ||
| 175 | - result: | ||
| 176 | - If the user approved intent it will return the **signature** and **id** fields | ||
| 177 | |||
| 178 | ```kotlin | ||
| 179 | val encryptedText = intent.data?.getStringExtra("signature") | ||
| 180 | // the id you sent | ||
| 181 | val id = intent.data?.getStringExtra("id") | ||
| 182 | ``` | ||
| 183 | |||
| 184 | - **nip04_decrypt** | ||
| 185 | - params: | ||
| 186 | |||
| 187 | ```kotlin | ||
| 188 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$encryptedText")) | ||
| 189 | intent.`package` = "com.example.signer" | ||
| 190 | intent.putExtra("type", "nip04_decrypt") | ||
| 191 | // to control the result in your application in case you are not waiting the result before sending another intent | ||
| 192 | intent.putExtra("id", "some_id") | ||
| 193 | // Send the current logged in user npub | ||
| 194 | intent.putExtra("current_user", account.keyPair.pubKey.toNpub()) | ||
| 195 | // Send the hex pubKey that will be used for decrypting the data | ||
| 196 | intent.putExtra("pubKey", pubKey) | ||
| 197 | |||
| 198 | context.startActivity(intent) | ||
| 199 | ``` | ||
| 200 | - result: | ||
| 201 | - If the user approved intent it will return the **signature** and **id** fields | ||
| 202 | |||
| 203 | ```kotlin | ||
| 204 | val plainText = intent.data?.getStringExtra("signature") | ||
| 205 | // the id you sent | ||
| 206 | val id = intent.data?.getStringExtra("id") | ||
| 207 | ``` | ||
| 208 | |||
| 209 | - **nip44_decrypt** | ||
| 210 | - params: | ||
| 211 | |||
| 212 | ```kotlin | ||
| 213 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$encryptedText")) | ||
| 214 | intent.`package` = "com.example.signer" | ||
| 215 | intent.putExtra("type", "nip04_decrypt") | ||
| 216 | // to control the result in your application in case you are not waiting the result before sending another intent | ||
| 217 | intent.putExtra("id", "some_id") | ||
| 218 | // Send the current logged in user npub | ||
| 219 | intent.putExtra("current_user", account.keyPair.pubKey.toNpub()) | ||
| 220 | // Send the hex pubKey that will be used for decrypting the data | ||
| 221 | intent.putExtra("pubKey", pubKey) | ||
| 222 | |||
| 223 | context.startActivity(intent) | ||
| 224 | ``` | ||
| 225 | - result: | ||
| 226 | - If the user approved intent it will return the **signature** and **id** fields | ||
| 227 | |||
| 228 | ```kotlin | ||
| 229 | val plainText = intent.data?.getStringExtra("signature") | ||
| 230 | // the id you sent | ||
| 231 | val id = intent.data?.getStringExtra("id") | ||
| 232 | ``` | ||
| 233 | |||
| 234 | - **decrypt_zap_event** | ||
| 235 | - params: | ||
| 236 | |||
| 237 | ```kotlin | ||
| 238 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$eventJson")) | ||
| 239 | intent.`package` = "com.example.signer" | ||
| 240 | intent.putExtra("type", "decrypt_zap_event") | ||
| 241 | // to control the result in your application in case you are not waiting the result before sending another intent | ||
| 242 | intent.putExtra("id", "some_id") | ||
| 243 | // Send the current logged in user npub | ||
| 244 | intent.putExtra("current_user", account.keyPair.pubKey.toNpub()) | ||
| 245 | context.startActivity(intent) | ||
| 246 | ``` | ||
| 247 | - result: | ||
| 248 | - If the user approved intent it will return the **signature** and **id** fields | ||
| 249 | |||
| 250 | ```kotlin | ||
| 251 | val eventJson = intent.data?.getStringExtra("signature") | ||
| 252 | // the id you sent | ||
| 253 | val id = intent.data?.getStringExtra("id") | ||
| 254 | ``` | ||
| 255 | |||
| 256 | ## Using Content Resolver | ||
| 257 | |||
| 258 | To get the result back from Signer Application you should use contentResolver.query in Kotlin. If you are using another framework check the documentation of your framework or a third party library to get the result. | ||
| 259 | |||
| 260 | If the user did not check the "remember my choice" option, the npub is not in Signer Application or the signer type is not recognized the `contentResolver` will return null | ||
| 261 | |||
| 262 | For the SIGN_EVENT type Signer Application returns two columns "signature" and "event". The column event is the signed event json | ||
| 263 | |||
| 264 | For the other types Signer Application returns the column "signature" | ||
| 265 | |||
| 266 | If the user chose to always reject the event, signer application will return the column "rejected" and you should not open signer application | ||
| 267 | |||
| 268 | ### Methods | ||
| 269 | |||
| 270 | - **get_public_key** | ||
| 271 | - params: | ||
| 272 | |||
| 273 | ```kotlin | ||
| 274 | val result = context.contentResolver.query( | ||
| 275 | Uri.parse("content://com.example.signer.GET_PUBLIC_KEY"), | ||
| 276 | listOf("login"), | ||
| 277 | null, | ||
| 278 | null, | ||
| 279 | null | ||
| 280 | ) | ||
| 281 | ``` | ||
| 282 | - result: | ||
| 283 | - Will return the **npub** in the signature column | ||
| 284 | |||
| 285 | ```kotlin | ||
| 286 | if (result == null) return | ||
| 287 | |||
| 288 | if (result.moveToFirst()) { | ||
| 289 | val index = it.getColumnIndex("signature") | ||
| 290 | if (index < 0) return | ||
| 291 | val npub = it.getString(index) | ||
| 292 | } | ||
| 293 | ``` | ||
| 294 | |||
| 295 | - **sign_event** | ||
| 296 | - params: | ||
| 297 | |||
| 298 | ```kotlin | ||
| 299 | val result = context.contentResolver.query( | ||
| 300 | Uri.parse("content://com.example.signer.SIGN_EVENT"), | ||
| 301 | listOf("$eventJson", "", "${logged_in_user_npub}"), | ||
| 302 | null, | ||
| 303 | null, | ||
| 304 | null | ||
| 305 | ) | ||
| 306 | ``` | ||
| 307 | - result: | ||
| 308 | - Will return the **signature** and the **event** columns | ||
| 309 | |||
| 310 | ```kotlin | ||
| 311 | if (result == null) return | ||
| 312 | |||
| 313 | if (result.moveToFirst()) { | ||
| 314 | val index = it.getColumnIndex("signature") | ||
| 315 | val indexJson = it.getColumnIndex("event") | ||
| 316 | val signature = it.getString(index) | ||
| 317 | val eventJson = it.getString(indexJson) | ||
| 318 | } | ||
| 319 | ``` | ||
| 320 | |||
| 321 | - **nip04_encrypt** | ||
| 322 | - params: | ||
| 323 | |||
| 324 | ```kotlin | ||
| 325 | val result = context.contentResolver.query( | ||
| 326 | Uri.parse("content://com.example.signer.NIP04_ENCRYPT"), | ||
| 327 | listOf("$plainText", "${hex_pub_key}", "${logged_in_user_npub}"), | ||
| 328 | null, | ||
| 329 | null, | ||
| 330 | null | ||
| 331 | ) | ||
| 332 | ``` | ||
| 333 | - result: | ||
| 334 | - Will return the **signature** column | ||
| 335 | |||
| 336 | ```kotlin | ||
| 337 | if (result == null) return | ||
| 338 | |||
| 339 | if (result.moveToFirst()) { | ||
| 340 | val index = it.getColumnIndex("signature") | ||
| 341 | val encryptedText = it.getString(index) | ||
| 342 | } | ||
| 343 | ``` | ||
| 344 | |||
| 345 | - **nip44_encrypt** | ||
| 346 | - params: | ||
| 347 | |||
| 348 | ```kotlin | ||
| 349 | val result = context.contentResolver.query( | ||
| 350 | Uri.parse("content://com.example.signer.NIP44_ENCRYPT"), | ||
| 351 | listOf("$plainText", "${hex_pub_key}", "${logged_in_user_npub}"), | ||
| 352 | null, | ||
| 353 | null, | ||
| 354 | null | ||
| 355 | ) | ||
| 356 | ``` | ||
| 357 | - result: | ||
| 358 | - Will return the **signature** column | ||
| 359 | |||
| 360 | ```kotlin | ||
| 361 | if (result == null) return | ||
| 362 | |||
| 363 | if (result.moveToFirst()) { | ||
| 364 | val index = it.getColumnIndex("signature") | ||
| 365 | val encryptedText = it.getString(index) | ||
| 366 | } | ||
| 367 | ``` | ||
| 368 | |||
| 369 | - **nip04_decrypt** | ||
| 370 | - params: | ||
| 371 | |||
| 372 | ```kotlin | ||
| 373 | val result = context.contentResolver.query( | ||
| 374 | Uri.parse("content://com.example.signer.NIP04_DECRYPT"), | ||
| 375 | listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_npub}"), | ||
| 376 | null, | ||
| 377 | null, | ||
| 378 | null | ||
| 379 | ) | ||
| 380 | ``` | ||
| 381 | - result: | ||
| 382 | - Will return the **signature** column | ||
| 383 | |||
| 384 | ```kotlin | ||
| 385 | if (result == null) return | ||
| 386 | |||
| 387 | if (result.moveToFirst()) { | ||
| 388 | val index = it.getColumnIndex("signature") | ||
| 389 | val encryptedText = it.getString(index) | ||
| 390 | } | ||
| 391 | ``` | ||
| 392 | |||
| 393 | - **nip44_decrypt** | ||
| 394 | - params: | ||
| 395 | |||
| 396 | ```kotlin | ||
| 397 | val result = context.contentResolver.query( | ||
| 398 | Uri.parse("content://com.example.signer.NIP44_DECRYPT"), | ||
| 399 | listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_npub}"), | ||
| 400 | null, | ||
| 401 | null, | ||
| 402 | null | ||
| 403 | ) | ||
| 404 | ``` | ||
| 405 | - result: | ||
| 406 | - Will return the **signature** column | ||
| 407 | |||
| 408 | ```kotlin | ||
| 409 | if (result == null) return | ||
| 410 | |||
| 411 | if (result.moveToFirst()) { | ||
| 412 | val index = it.getColumnIndex("signature") | ||
| 413 | val encryptedText = it.getString(index) | ||
| 414 | } | ||
| 415 | ``` | ||
| 416 | |||
| 417 | - **decrypt_zap_event** | ||
| 418 | - params: | ||
| 419 | |||
| 420 | ```kotlin | ||
| 421 | val result = context.contentResolver.query( | ||
| 422 | Uri.parse("content://com.example.signer.DECRYPT_ZAP_EVENT"), | ||
| 423 | listOf("$eventJson", "", "${logged_in_user_npub}"), | ||
| 424 | null, | ||
| 425 | null, | ||
| 426 | null | ||
| 427 | ) | ||
| 428 | ``` | ||
| 429 | - result: | ||
| 430 | - Will return the **signature** column | ||
| 431 | |||
| 432 | ```kotlin | ||
| 433 | if (result == null) return | ||
| 434 | |||
| 435 | if (result.moveToFirst()) { | ||
| 436 | val index = it.getColumnIndex("signature") | ||
| 437 | val eventJson = it.getString(index) | ||
| 438 | } | ||
| 439 | ``` | ||
| 440 | |||
| 441 | # Usage for Web Applications | ||
| 442 | |||
| 443 | Since web applications can't receive a result from the intent, you should add a modal to paste the signature or the event json or create a callback url. | ||
| 444 | |||
| 445 | If you send the callback url parameter, Signer Application will send the result to the url. | ||
| 446 | |||
| 447 | If you don't send a callback url, Signer Application will copy the result to the clipboard. | ||
| 448 | |||
| 449 | You can configure the `returnType` to be **signature** or **event**. | ||
| 450 | |||
| 451 | Android intents and browser urls have limitations, so if you are using the `returnType` of **event** consider using the parameter **compressionType=gzip** that will return "Signer1" + Base64 gzip encoded event json | ||
| 452 | |||
| 453 | ## Methods | ||
| 454 | |||
| 455 | - **get_public_key** | ||
| 456 | - params: | ||
| 457 | |||
| 458 | ```js | ||
| 459 | window.href = `nostrsigner:?compressionType=none&returnType=signature&type=get_public_key&callbackUrl=https://example.com/?event=`; | ||
| 460 | ``` | ||
| 461 | |||
| 462 | - **sign_event** | ||
| 463 | - params: | ||
| 464 | |||
| 465 | ```js | ||
| 466 | window.href = `nostrsigner:${eventJson}?compressionType=none&returnType=signature&type=sign_event&callbackUrl=https://example.com/?event=`; | ||
| 467 | ``` | ||
| 468 | |||
| 469 | - **nip04_encrypt** | ||
| 470 | - params: | ||
| 471 | |||
| 472 | ```js | ||
| 473 | window.href = `nostrsigner:${plainText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_encrypt&callbackUrl=https://example.com/?event=`; | ||
| 474 | ``` | ||
| 475 | |||
| 476 | - **nip44_encrypt** | ||
| 477 | - params: | ||
| 478 | |||
| 479 | ```js | ||
| 480 | window.href = `nostrsigner:${plainText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_encrypt&callbackUrl=https://example.com/?event=`; | ||
| 481 | ``` | ||
| 482 | |||
| 483 | - **nip04_decrypt** | ||
| 484 | - params: | ||
| 485 | |||
| 486 | ```js | ||
| 487 | window.href = `nostrsigner:${encryptedText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_decrypt&callbackUrl=https://example.com/?event=`; | ||
| 488 | ``` | ||
| 489 | |||
| 490 | - **nip44_decrypt** | ||
| 491 | - params: | ||
| 492 | |||
| 493 | ```js | ||
| 494 | window.href = `nostrsigner:${encryptedText}?pubKey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_decrypt&callbackUrl=https://example.com/?event=`; | ||
| 495 | ``` | ||
| 496 | |||
| 497 | - **decrypt_zap_event** | ||
| 498 | - params: | ||
| 499 | |||
| 500 | ```js | ||
| 501 | window.href = `nostrsigner:${eventJson}?compressionType=none&returnType=signature&type=decrypt_zap_event&callbackUrl=https://example.com/?event=`; | ||
| 502 | ``` | ||
| 503 | |||
| 504 | ## Example | ||
| 505 | |||
| 506 | ```js | ||
| 507 | <!DOCTYPE html> | ||
| 508 | <html lang="en"> | ||
| 509 | <head> | ||
| 510 | <meta charset="UTF-8"> | ||
| 511 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 512 | <title>Document</title> | ||
| 513 | </head> | ||
| 514 | <body> | ||
| 515 | <h1>Test</h1> | ||
| 516 | |||
| 517 | <script> | ||
| 518 | window.onload = function() { | ||
| 519 | var url = new URL(window.location.href); | ||
| 520 | var params = url.searchParams; | ||
| 521 | if (params) { | ||
| 522 | var param1 = params.get("event"); | ||
| 523 | if (param1) alert(param1) | ||
| 524 | } | ||
| 525 | let json = { | ||
| 526 | kind: 1, | ||
| 527 | content: "test" | ||
| 528 | } | ||
| 529 | let encodedJson = encodeURIComponent(JSON.stringify(json)) | ||
| 530 | var newAnchor = document.createElement("a"); | ||
| 531 | newAnchor.href = `nostrsigner:${encodedJson}?compressionType=none&returnType=signature&type=sign_event&callbackUrl=https://example.com/?event=`; | ||
| 532 | newAnchor.textContent = "Open External Signer"; | ||
| 533 | document.body.appendChild(newAnchor) | ||
| 534 | } | ||
| 535 | </script> | ||
| 536 | </body> | ||
| 537 | </html> | ||
| 538 | ``` | ||
| @@ -26,7 +26,6 @@ The list of tags are as follows: | |||
| 26 | * `m` a string indicating the data type of the file. The [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) format must be used, and they should be lowercase. | 26 | * `m` a string indicating the data type of the file. The [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) format must be used, and they should be lowercase. |
| 27 | * `title` (required) title of the video | 27 | * `title` (required) title of the video |
| 28 | * `"published_at"`, for the timestamp in unix seconds (stringified) of the first time the video was published | 28 | * `"published_at"`, for the timestamp in unix seconds (stringified) of the first time the video was published |
| 29 | * `"aes-256-gcm"` (optional) key and nonce for AES-GCM encryption with tagSize always 128bits | ||
| 30 | * `x` containing the SHA-256 hexencoded string of the file. | 29 | * `x` containing the SHA-256 hexencoded string of the file. |
| 31 | * `size` (optional) size of file in bytes | 30 | * `size` (optional) size of file in bytes |
| 32 | * `dim` (optional) size of file in pixels in the form `<width>x<height>` | 31 | * `dim` (optional) size of file in pixels in the form `<width>x<height>` |
| @@ -62,7 +61,6 @@ The list of tags are as follows: | |||
| 62 | ["url",<string with URI of file>], | 61 | ["url",<string with URI of file>], |
| 63 | ["m", <MIME type>], | 62 | ["m", <MIME type>], |
| 64 | ["x",<Hash SHA-256>], | 63 | ["x",<Hash SHA-256>], |
| 65 | ["aes-256-gcm",<key>, <iv>], | ||
| 66 | ["size", <size of file in bytes>], | 64 | ["size", <size of file in bytes>], |
| 67 | ["duration", <duration of video in seconds>], | 65 | ["duration", <duration of video in seconds>], |
| 68 | ["dim", <size of file in pixels>], | 66 | ["dim", <size of file in pixels>], |
diff --git a/BREAKING.md b/BREAKING.md index 7b48ee0..720d27b 100644 --- a/BREAKING.md +++ b/BREAKING.md | |||
| @@ -5,6 +5,7 @@ reverse chronological order. | |||
| 5 | 5 | ||
| 6 | | Date | Commit | NIP | Change | | 6 | | Date | Commit | NIP | Change | |
| 7 | | ----------- | --------- | -------- | ------ | | 7 | | ----------- | --------- | -------- | ------ | |
| 8 | | 2024-05-25 | [5d1d1c17](https://github.com/nostr-protocol/nips/commit/5d1d1c17) | [NIP-71](71.md) | 'aes-256-gcm' tag was removed | | ||
| 8 | | 2024-04-30 | [bad88262](https://github.com/nostr-protocol/nips/commit/bad88262) | [NIP-34](34.md) | 'earliest-unique-commit' tag was removed (use 'r' tag instead) | | 9 | | 2024-04-30 | [bad88262](https://github.com/nostr-protocol/nips/commit/bad88262) | [NIP-34](34.md) | 'earliest-unique-commit' tag was removed (use 'r' tag instead) | |
| 9 | | 2024-02-25 | [4a171cb0](https://github.com/nostr-protocol/nips/commit/4a171cb0) | [NIP-18](18.md) | quote repost should use `q` tag | | 10 | | 2024-02-25 | [4a171cb0](https://github.com/nostr-protocol/nips/commit/4a171cb0) | [NIP-18](18.md) | quote repost should use `q` tag | |
| 10 | | 2024-02-21 | [c6cd655c](https://github.com/nostr-protocol/nips/commit/c6cd655c) | [NIP-46](46.md) | Params were stringified | | 11 | | 2024-02-21 | [c6cd655c](https://github.com/nostr-protocol/nips/commit/c6cd655c) | [NIP-46](46.md) | Params were stringified | |
| @@ -51,6 +51,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 51 | - [NIP-31: Dealing with Unknown Events](31.md) | 51 | - [NIP-31: Dealing with Unknown Events](31.md) |
| 52 | - [NIP-32: Labeling](32.md) | 52 | - [NIP-32: Labeling](32.md) |
| 53 | - [NIP-34: `git` stuff](34.md) | 53 | - [NIP-34: `git` stuff](34.md) |
| 54 | - [NIP-35: Torrents](35.md) | ||
| 54 | - [NIP-36: Sensitive Content](36.md) | 55 | - [NIP-36: Sensitive Content](36.md) |
| 55 | - [NIP-38: User Statuses](38.md) | 56 | - [NIP-38: User Statuses](38.md) |
| 56 | - [NIP-39: External Identities in Profiles](39.md) | 57 | - [NIP-39: External Identities in Profiles](39.md) |
| @@ -67,6 +68,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 67 | - [NIP-52: Calendar Events](52.md) | 68 | - [NIP-52: Calendar Events](52.md) |
| 68 | - [NIP-53: Live Activities](53.md) | 69 | - [NIP-53: Live Activities](53.md) |
| 69 | - [NIP-54: Wiki](54.md) | 70 | - [NIP-54: Wiki](54.md) |
| 71 | - [NIP-55: Android Signer Application](100.md) | ||
| 70 | - [NIP-56: Reporting](56.md) | 72 | - [NIP-56: Reporting](56.md) |
| 71 | - [NIP-57: Lightning Zaps](57.md) | 73 | - [NIP-57: Lightning Zaps](57.md) |
| 72 | - [NIP-58: Badges](58.md) | 74 | - [NIP-58: Badges](58.md) |
| @@ -123,6 +125,9 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 123 | | `1971` | Problem Tracker | [nostrocket][nostrocket] | | 125 | | `1971` | Problem Tracker | [nostrocket][nostrocket] | |
| 124 | | `1984` | Reporting | [56](56.md) | | 126 | | `1984` | Reporting | [56](56.md) | |
| 125 | | `1985` | Label | [32](32.md) | | 127 | | `1985` | Label | [32](32.md) | |
| 128 | | `2003` | Torrent | [35](35.md) | | ||
| 129 | | `2004` | Torrent Comment | [35](35.md) | | ||
| 130 | | `2022` | Coinjoin Pool | [joinstr][joinstr] | | ||
| 126 | | `4550` | Community Post Approval | [72](72.md) | | 131 | | `4550` | Community Post Approval | [72](72.md) | |
| 127 | | `5000`-`5999` | Job Request | [90](90.md) | | 132 | | `5000`-`5999` | Job Request | [90](90.md) | |
| 128 | | `6000`-`6999` | Job Result | [90](90.md) | | 133 | | `6000`-`6999` | Job Result | [90](90.md) | |
| @@ -177,6 +182,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 177 | | `30617` | Repository announcements | [34](34.md) | | 182 | | `30617` | Repository announcements | [34](34.md) | |
| 178 | | `30818` | Wiki article | [54](54.md) | | 183 | | `30818` | Wiki article | [54](54.md) | |
| 179 | | `30819` | Redirects | [54](54.md) | | 184 | | `30819` | Redirects | [54](54.md) | |
| 185 | | `31890` | Feed | [NUD: Custom Feeds](https://wikifreedia.xyz/cip-01/97c70a44366a6535c1) | | ||
| 180 | | `31922` | Date-Based Calendar Event | [52](52.md) | | 186 | | `31922` | Date-Based Calendar Event | [52](52.md) | |
| 181 | | `31923` | Time-Based Calendar Event | [52](52.md) | | 187 | | `31923` | Time-Based Calendar Event | [52](52.md) | |
| 182 | | `31924` | Calendar | [52](52.md) | | 188 | | `31924` | Calendar | [52](52.md) | |
| @@ -189,8 +195,10 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 189 | | `34550` | Community Definition | [72](72.md) | | 195 | | `34550` | Community Definition | [72](72.md) | |
| 190 | | `39000-9` | Group metadata events | [29](29.md) | | 196 | | `39000-9` | Group metadata events | [29](29.md) | |
| 191 | 197 | ||
| 198 | [NUD: Custom Feeds]: https://wikifreedia.xyz/cip-01/97c70a44366a6535c1 | ||
| 192 | [nostrocket]: https://github.com/nostrocket/NIPS/blob/main/Problems.md | 199 | [nostrocket]: https://github.com/nostrocket/NIPS/blob/main/Problems.md |
| 193 | [lnpub]: https://github.com/shocknet/Lightning.Pub/blob/master/proto/autogenerated/client.md | 200 | [lnpub]: https://github.com/shocknet/Lightning.Pub/blob/master/proto/autogenerated/client.md |
| 201 | [joinstr]: https://gitlab.com/1440000bytes/joinstr/-/blob/main/NIP.md | ||
| 194 | 202 | ||
| 195 | ## Message types | 203 | ## Message types |
| 196 | 204 | ||
| @@ -216,62 +224,62 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | |||
| 216 | | `AUTH` | used to send authentication challenges | [42](42.md) | | 224 | | `AUTH` | used to send authentication challenges | [42](42.md) | |
| 217 | | `COUNT` | used to send requested event counts to clients | [45](45.md) | | 225 | | `COUNT` | used to send requested event counts to clients | [45](45.md) | |
| 218 | 226 | ||
| 219 | Please update these lists when proposing NIPs introducing new event kinds. | ||
| 220 | |||
| 221 | ## Standardized Tags | 227 | ## Standardized Tags |
| 222 | 228 | ||
| 223 | | name | value | other parameters | NIP | | 229 | | name | value | other parameters | NIP | |
| 224 | | ----------------- | ------------------------------------ | -------------------- | ------------------------------------- | | 230 | | ----------------- | ------------------------------------ | ------------------------------- | ------------------------------------- | |
| 225 | | `e` | event id (hex) | relay URL, marker | [01](01.md), [10](10.md) | | 231 | | `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) | |
| 226 | | `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) | | 232 | | `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) | |
| 227 | | `a` | coordinates to an event | relay URL | [01](01.md) | | 233 | | `a` | coordinates to an event | relay URL | [01](01.md) | |
| 228 | | `d` | identifier | -- | [01](01.md) | | 234 | | `d` | identifier | -- | [01](01.md) | |
| 229 | | `g` | geohash | -- | [52](52.md) | | 235 | | `g` | geohash | -- | [52](52.md) | |
| 230 | | `i` | identity | proof | [39](39.md) | | 236 | | `i` | identity | proof | [39](39.md) | |
| 231 | | `k` | kind number (string) | -- | [18](18.md), [25](25.md), [72](72.md) | | 237 | | `k` | kind number (string) | -- | [18](18.md), [25](25.md), [72](72.md) | |
| 232 | | `l` | label, label namespace | annotations | [32](32.md) | | 238 | | `l` | label, label namespace | annotations | [32](32.md) | |
| 233 | | `L` | label namespace | -- | [32](32.md) | | 239 | | `L` | label namespace | -- | [32](32.md) | |
| 234 | | `m` | MIME type | -- | [94](94.md) | | 240 | | `m` | MIME type | -- | [94](94.md) | |
| 235 | | `q` | event id (hex) | relay URL | [18](18.md) | | 241 | | `q` | event id (hex) | relay URL | [18](18.md) | |
| 236 | | `r` | a reference (URL, etc) | petname | | | 242 | | `r` | a reference (URL, etc) | petname | [24](24.md) | |
| 237 | | `r` | relay url | marker | [65](65.md) | | 243 | | `r` | relay url | marker | [65](65.md) | |
| 238 | | `t` | hashtag | -- | | | 244 | | `t` | hashtag | -- | | |
| 239 | | `alt` | summary | -- | [31](31.md) | | 245 | | `alt` | summary | -- | [31](31.md) | |
| 240 | | `amount` | millisatoshis, stringified | -- | [57](57.md) | | 246 | | `amount` | millisatoshis, stringified | -- | [57](57.md) | |
| 241 | | `bolt11` | `bolt11` invoice | -- | [57](57.md) | | 247 | | `bolt11` | `bolt11` invoice | -- | [57](57.md) | |
| 242 | | `challenge` | challenge string | -- | [42](42.md) | | 248 | | `challenge` | challenge string | -- | [42](42.md) | |
| 243 | | `client` | name, address | relay URL | [89](89.md) | | 249 | | `client` | name, address | relay URL | [89](89.md) | |
| 244 | | `clone` | git clone URL | -- | [34](34.md) | | 250 | | `clone` | git clone URL | -- | [34](34.md) | |
| 245 | | `content-warning` | reason | -- | [36](36.md) | | 251 | | `content-warning` | reason | -- | [36](36.md) | |
| 246 | | `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | | 252 | | `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | |
| 247 | | `description` | description | -- | [34](34.md), [57](57.md), [58](58.md) | | 253 | | `description` | description | -- | [34](34.md), [57](57.md), [58](58.md) | |
| 248 | | `emoji` | shortcode, image URL | -- | [30](30.md) | | 254 | | `emoji` | shortcode, image URL | -- | [30](30.md) | |
| 249 | | `encrypted` | -- | -- | [90](90.md) | | 255 | | `encrypted` | -- | -- | [90](90.md) | |
| 250 | | `expiration` | unix timestamp (string) | -- | [40](40.md) | | 256 | | `expiration` | unix timestamp (string) | -- | [40](40.md) | |
| 251 | | `goal` | event id (hex) | relay URL | [75](75.md) | | 257 | | `goal` | event id (hex) | relay URL | [75](75.md) | |
| 252 | | `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) | | 258 | | `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) | |
| 253 | | `imeta` | inline metadata | -- | [92](92.md) | | 259 | | `imeta` | inline metadata | -- | [92](92.md) | |
| 254 | | `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | | 260 | | `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | |
| 255 | | `location` | location string | -- | [52](52.md), [99](99.md) | | 261 | | `location` | location string | -- | [52](52.md), [99](99.md) | |
| 256 | | `name` | name | -- | [34](34.md), [58](58.md) | | 262 | | `name` | name | -- | [34](34.md), [58](58.md) | |
| 257 | | `nonce` | random | -- | [13](13.md) | | 263 | | `nonce` | random | difficulty | [13](13.md) | |
| 258 | | `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | | 264 | | `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | |
| 259 | | `price` | price | currency, frequency | [99](99.md) | | 265 | | `price` | price | currency, frequency | [99](99.md) | |
| 260 | | `proxy` | external ID | protocol | [48](48.md) | | 266 | | `proxy` | external ID | protocol | [48](48.md) | |
| 261 | | `published_at` | unix timestamp (string) | -- | [23](23.md) | | 267 | | `published_at` | unix timestamp (string) | -- | [23](23.md) | |
| 262 | | `relay` | relay url | -- | [42](42.md), [17](17.md) | | 268 | | `relay` | relay url | -- | [42](42.md), [17](17.md) | |
| 263 | | `relays` | relay list | -- | [57](57.md) | | 269 | | `relays` | relay list | -- | [57](57.md) | |
| 264 | | `server` | file storage server url | -- | [96](96.md) | | 270 | | `server` | file storage server url | -- | [96](96.md) | |
| 265 | | `subject` | subject | -- | [14](14.md), [17](17.md) | | 271 | | `subject` | subject | -- | [14](14.md), [17](17.md) | |
| 266 | | `summary` | article summary | -- | [23](23.md) | | 272 | | `summary` | article summary | -- | [23](23.md) | |
| 267 | | `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | | 273 | | `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | |
| 268 | | `title` | article title | -- | [23](23.md) | | 274 | | `title` | article title | -- | [23](23.md) | |
| 269 | | `web` | webpage URL | -- | [34](34.md) | | 275 | | `web` | webpage URL | -- | [34](34.md) | |
| 270 | | `zap` | pubkey (hex), relay URL | weight | [57](57.md) | | 276 | | `zap` | pubkey (hex), relay URL | weight | [57](57.md) | |
| 277 | |||
| 278 | Please update these lists when proposing new NIPs. | ||
| 271 | 279 | ||
| 272 | ## Criteria for acceptance of NIPs | 280 | ## Criteria for acceptance of NIPs |
| 273 | 281 | ||
| 274 | 1. They should be implemented in at least two clients and one relay -- when applicable. | 282 | 1. They should be fully implemented in at least two clients and one relay -- when applicable. |
| 275 | 2. They should make sense. | 283 | 2. They should make sense. |
| 276 | 3. They should be optional and backwards-compatible: care must be taken such that clients and relays that choose to not implement them do not stop working when interacting with the ones that choose to. | 284 | 3. They should be optional and backwards-compatible: care must be taken such that clients and relays that choose to not implement them do not stop working when interacting with the ones that choose to. |
| 277 | 4. There should be no more than one way of doing the same thing. | 285 | 4. There should be no more than one way of doing the same thing. |