mirror of
https://github.com/nostr-protocol/nips.git
synced 2025-12-09 00:28:51 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dd66f0661 | ||
|
|
3bbbe3ad71 |
2
07.md
2
07.md
@@ -20,8 +20,6 @@ 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.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.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
|
||||
|
||||
7
18.md
7
18.md
@@ -20,10 +20,9 @@ reposted.
|
||||
|
||||
## Quote Reposts
|
||||
|
||||
Quote reposts are `kind 1` events with an embedded `q` tag of the note being
|
||||
quote reposted. The `q` tag ensures quote reposts are not pulled and included
|
||||
as replies in threads. It also allows you to easily pull and count all of the
|
||||
quotes for a post.
|
||||
Quote reposts are `kind 1` events with an embedded `e` tag
|
||||
(see [NIP-08](08.md) and [NIP-27](27.md)). Because a quote repost includes
|
||||
an `e` tag, it may show up along replies to the reposted note.
|
||||
|
||||
## Generic Reposts
|
||||
|
||||
|
||||
59
302.md
Normal file
59
302.md
Normal file
@@ -0,0 +1,59 @@
|
||||
NIP-302
|
||||
=========
|
||||
|
||||
Relay Pools
|
||||
-----------
|
||||
|
||||
`draft` `optional`
|
||||
|
||||
# Introduction
|
||||
|
||||
This NIP introduces a system for the creation, management, and utilization of relay pools.
|
||||
|
||||
# Specification
|
||||
|
||||
## Creating a Relay Pool
|
||||
|
||||
Users initiate a relay pool by creating a Nostr event with `kind` 30010.
|
||||
- The `content` field is the description of the relay pool.
|
||||
- A `d` tag has the name of the relay pool (e.g. awesome-pool) as value.
|
||||
- At least one `r` tag MUST be present with the third value as `"pool"`. These pool addresses are URLs clients can connect to.
|
||||
- Zero or more `r` tags with third value as `"member"`, empty string, or missing signify a relay pool member.
|
||||
- Zero or more `auth-required` tags with one of the following values: `nip-42` or `nip-98`. Clients wishing to connect to Relay Pools with an `auth-required` tag MUST support at least one of the NIPs before initiating a connection.
|
||||
|
||||
## Updating the Relay Pool
|
||||
|
||||
To update the relay member list of a relay pool, pool addresses, a new event of kind 30010 with the same `d` tag MUST be published.
|
||||
Pool addresses and member relays are added or removed at any time.
|
||||
|
||||
## Joining a Relay Pool
|
||||
|
||||
Relay operators wishing to join a relay pool publish a Relay Pool Join Request event with `kind` 8000:
|
||||
- `p` tag referencing the pubkey of the Relay Pool (event kind 30010).
|
||||
- `a` tag addressing the kind 30010 event.
|
||||
- a single `r` tag with the relay address (e.g., wss://cool-relay.cool-domain.com).
|
||||
|
||||
## Inclusion in the Relay Pool
|
||||
|
||||
Relay Pool Operators MAY include new member relays by updating the Relay Pool event and adding their `r` tag from the kind 8000 event.
|
||||
|
||||
### Leaving a Relay Pool
|
||||
|
||||
Member Relay Operators can request removal from a relay pool by publishing an Event Deletion (kind 5) referencing their Relay Pool Join Request event (kind 8000).
|
||||
Upon receiving a kind 5 event, the Relay Pool Operator SHOULD issue a new event of kind 30010, removing the `r` tag that references the parting relay.
|
||||
|
||||
# Verification
|
||||
|
||||
Relay Pool Operators MAY require further steps as part of the application process (e.g. proof of work (mined event), payment or out-of-channel communication).
|
||||
Relay Pool Operators MAY respond with kind 1 note to Relay Pool Join Requests events by referencing the event kind 8000 and/or tagging the requester's pubkey.
|
||||
A Relay Pool is only successfully joined once a new Relay Pool event is published including the `r` tag from the Relay Pool Join Request.
|
||||
|
||||
# Client Connection
|
||||
|
||||
Clients can discover relay pools by subscribing to events of kind 30010.
|
||||
Clients MUST connect directly to the relay pool addresses in order to be routed to member relays.
|
||||
Relay Pools SHOULD distribute client connections among Relay Pool Members using a fair algorithm.
|
||||
|
||||
## Proxying and Authentication
|
||||
Relay Pools MAY serve incoming WebSockets connections either by passthrough or by redirect.
|
||||
Relay Pools that require authentication (e.g. for paid relay pools) MUST support either [NIP-42](42.md), [NIP-98](98.md) or both.
|
||||
260
46.md
260
46.md
@@ -1,222 +1,98 @@
|
||||
# NIP-46 - Nostr Remote Signing
|
||||
NIP-46
|
||||
======
|
||||
|
||||
## Rationale
|
||||
Nostr Connect
|
||||
-------------
|
||||
|
||||
Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
|
||||
`draft` `optional`
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Terminology
|
||||
## Signer Discovery
|
||||
|
||||
- **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.
|
||||
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.
|
||||
|
||||
All pubkeys specified in this NIP are in hex format.
|
||||
### Started by the signer (nsecBunker)
|
||||
|
||||
## 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:
|
||||
The remote signer generates a connection token in the form
|
||||
|
||||
```
|
||||
bunker://<remote-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
|
||||
bunker://<hex-pubkey>?relay=wss://...&relay=wss://...&secret=<optional-secret>
|
||||
```
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
### Direct connection initiated by the client
|
||||
### Started by the client
|
||||
|
||||
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).
|
||||
The client generates a QR code in the following form (URL-encoded):
|
||||
|
||||
```
|
||||
nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}>
|
||||
nostrconnect://<client-key-hex>?relay=wss://...&metadata={"name":"...", "url": "...", "description": "..."}
|
||||
```
|
||||
|
||||
## The flow
|
||||
The signer scans the QR code and sends a `connect` message to the client in the specified relays.
|
||||
|
||||
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
|
||||
|
||||
### Example flow for signing an event
|
||||
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).
|
||||
|
||||
- Remote user pubkey (e.g. signing as) `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
|
||||
- Local pubkey is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
|
||||
Events sent by the client to the remote signer have the following format:
|
||||
|
||||
#### Signature request
|
||||
|
||||
```json
|
||||
```js
|
||||
{
|
||||
"kind": 24133,
|
||||
"pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
|
||||
"content": nip04({
|
||||
"id": <random_string>,
|
||||
"method": "sign_event",
|
||||
"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
|
||||
"pubkey": "<client-key-hex>"
|
||||
"kind": 24133,
|
||||
"tags": [
|
||||
["p", "<signer-key-hex>"]
|
||||
],
|
||||
"content": "nip04_encrypted_json({id: <random-string>, method: <see-below>, params: [array_of_strings]})",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Response event
|
||||
And the events the remote signer sends to the client have the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 24133,
|
||||
"pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
|
||||
"content": nip04({
|
||||
"id": <random_string>,
|
||||
"result": json_stringified(<signed-event>)
|
||||
}),
|
||||
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the local keypair pubkey
|
||||
}
|
||||
```js
|
||||
"pubkey": "<signer-key-hex>"
|
||||
"kind": 24133,
|
||||
"tags": [
|
||||
["p", "<client-key-hex>"]
|
||||
],
|
||||
"content": "nip04_encrypted_json({id: <request-id>, result: <string>, error: <reason-string>})",
|
||||
...
|
||||
```
|
||||
|
||||
#### Diagram
|
||||
The signer key will always be the key of the user who controls the signer device.
|
||||
|
||||

|
||||
### Methods
|
||||
|
||||
## Request Events `kind: 24133`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": <id>,
|
||||
"kind": 24133,
|
||||
"pubkey": <local_keypair_pubkey>,
|
||||
"content": <nip04(<request>)>,
|
||||
"tags": [["p", <remote_user_pubkey>]], // NB: in the `create_account` event, the remote signer pubkey should be `p` tagged.
|
||||
"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": <random_string>,
|
||||
"method": <method_name>,
|
||||
"params": [array_of_strings]
|
||||
}
|
||||
```
|
||||
|
||||
- `id` is a random string that is a request ID. This same ID will be sent back in the response payload.
|
||||
- `method` is the name of the method/command (detailed below).
|
||||
- `params` is a positional array of string parameters.
|
||||
|
||||
### Methods/Commands
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
## 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)
|
||||
- **connect**
|
||||
- params: [`pubkey`, `secret`]
|
||||
- result: `"ack"`
|
||||
- **get_public_key**
|
||||
- params: []
|
||||
- result: `pubkey-hex`
|
||||
- **sign_event**
|
||||
- params: [`event`]
|
||||
- result: `json_string(event_with_pubkey_id_and_signature)`
|
||||
- **get_relays**
|
||||
- params: []
|
||||
- result: `json_string({[url: string]: {read: boolean, write: boolean}})`
|
||||
- **nip04_encrypt**
|
||||
- params: [`third-party-pubkey`, `plaintext`]
|
||||
- result: `nip04-ciphertext`
|
||||
- **nip04_decrypt**
|
||||
- params: [`third-party-pubkey`, `nip04-ciphertext`]
|
||||
- result: `plaintext`
|
||||
- **nip44_get_key**
|
||||
- params: [`third-party-pubkey`]
|
||||
- result: `nip44-conversation-key`
|
||||
- **nip44_encrypt**
|
||||
- params: [`third-party-pubkey`, `plaintext`]
|
||||
- result: `nip44-ciphertext`
|
||||
- **nip44_decrypt**
|
||||
- params: [`third-party-pubkey`, `nip44-ciphertext`]
|
||||
- result: `plaintext`
|
||||
- **ping**
|
||||
- params: []
|
||||
- result: `"pong"`
|
||||
|
||||
34
49.md
34
49.md
@@ -12,17 +12,17 @@ This NIP defines a method by which clients can encrypt (and decrypt) a user's pr
|
||||
Symmetric Encryption Key derivation
|
||||
-----------------------------------
|
||||
|
||||
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.
|
||||
PASSWORD = read from the user
|
||||
|
||||
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 |
|
||||
|-------|-----------------|----------------------------- |
|
||||
| 16 | 64 MiB | 100 ms |
|
||||
| 18 | 256 MiB | |
|
||||
| 20 | 1 GiB | 2 seconds |
|
||||
| 21 | 2 GiB | |
|
||||
| 22 | 4 GiB | |
|
||||
| LOG\_N | MEMORY REQUIRED | APPROX TIME ON FAST COMPUTER |
|
||||
|--------|-----------------|----------------------------- |
|
||||
| 16 | 64 MiB | 100 ms |
|
||||
| 18 | 256 MiB | |
|
||||
| 20 | 1 GiB | 2 seconds |
|
||||
| 21 | 2 GiB | |
|
||||
| 22 | 4 GiB | |
|
||||
|
||||
SALT = 16 random bytes
|
||||
|
||||
@@ -78,22 +78,6 @@ The decryption process operates in the reverse.
|
||||
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:
|
||||
|
||||
`ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p`
|
||||
@@ -102,6 +86,8 @@ When decrypted with password='nostr' and log_n=16 yields the following hex-encod
|
||||
|
||||
`3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683`
|
||||
|
||||
The reverse process is non-deterministic due to the random nonce.
|
||||
|
||||
Discussion
|
||||
----------
|
||||
|
||||
|
||||
25
51.md
25
51.md
@@ -49,7 +49,6 @@ 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) |
|
||||
| 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)) |
|
||||
| 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
|
||||
|
||||
@@ -104,30 +103,6 @@ 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
|
||||
|
||||
```scala
|
||||
|
||||
80
54.md
80
54.md
@@ -1,80 +0,0 @@
|
||||
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
2
65.md
@@ -19,7 +19,7 @@ The `.content` is not used.
|
||||
["r", "wss://alicerelay.example.com"],
|
||||
["r", "wss://brando-relay.com"],
|
||||
["r", "wss://expensive-relay.example2.com", "write"],
|
||||
["r", "wss://nostr-relay.example.com", "read"]
|
||||
["r", "wss://nostr-relay.example.com", "read"],
|
||||
],
|
||||
"content": "",
|
||||
...other fields
|
||||
|
||||
2
92.md
2
92.md
@@ -41,5 +41,3 @@ after the file is uploaded and included in the post.
|
||||
|
||||
When pasting URLs during post composition, the client MAY download the file
|
||||
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
45
BREAKING.md
@@ -1,45 +0,0 @@
|
||||
# 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.
|
||||
13
README.md
13
README.md
@@ -15,7 +15,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
- [Criteria for acceptance of NIPs](#criteria-for-acceptance-of-nips)
|
||||
- [Is this repository a centralizing factor?](#is-this-repository-a-centralizing-factor)
|
||||
- [How this repository works](#how-this-repository-works)
|
||||
- [Breaking Changes](#breaking-changes)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
@@ -79,6 +78,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
- [NIP-96: HTTP File Storage Integration](96.md)
|
||||
- [NIP-98: HTTP Auth](98.md)
|
||||
- [NIP-99: Classified Listings](99.md)
|
||||
- [NIP-302: Relay Pools](302.md)
|
||||
|
||||
## Event Kinds
|
||||
| kind | description | NIP |
|
||||
@@ -92,7 +92,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `6` | Repost | [18](18.md) |
|
||||
| `7` | Reaction | [25](25.md) |
|
||||
| `8` | Badge Award | [58](58.md) |
|
||||
| `13` | Seal | [59](59.md) |
|
||||
| `16` | Generic Repost | [18](18.md) |
|
||||
| `40` | Channel Creation | [28](28.md) |
|
||||
| `41` | Channel Metadata | [28](28.md) |
|
||||
@@ -102,7 +101,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `1021` | Bid | [15](15.md) |
|
||||
| `1022` | Bid confirmation | [15](15.md) |
|
||||
| `1040` | OpenTimestamps | [03](03.md) |
|
||||
| `1059` | Gift Wrap | [59](59.md) |
|
||||
| `1063` | File Metadata | [94](94.md) |
|
||||
| `1311` | Live Chat Message | [53](53.md) |
|
||||
| `1971` | Problem Tracker | [nostrocket][nostrocket] |
|
||||
@@ -112,6 +110,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `5000`-`5999` | Job Request | [90](90.md) |
|
||||
| `6000`-`6999` | Job Result | [90](90.md) |
|
||||
| `7000` | Job Feedback | [90](90.md) |
|
||||
| `8000` | Relay Pool Join Request | [302](302.md) |
|
||||
| `9041` | Zap Goal | [75](75.md) |
|
||||
| `9734` | Zap Request | [57](57.md) |
|
||||
| `9735` | Zap | [57](57.md) |
|
||||
@@ -141,6 +140,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `30004` | Curation sets | [51](51.md) |
|
||||
| `30008` | Profile Badges | [58](58.md) |
|
||||
| `30009` | Badge Definition | [58](58.md) |
|
||||
| `30010` | Relay Pool | [302](302.md) |
|
||||
| `30015` | Interest sets | [51](51.md) |
|
||||
| `30017` | Create or update a stall | [15](15.md) |
|
||||
| `30018` | Create or update a product | [15](15.md) |
|
||||
@@ -149,7 +149,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
||||
| `30023` | Long-form Content | [23](23.md) |
|
||||
| `30024` | Draft Long-form Content | [23](23.md) |
|
||||
| `30030` | Emoji sets | [51](51.md) |
|
||||
| `30063` | Release artifact sets | [51](51.md) |
|
||||
| `30078` | Application-specific Data | [78](78.md) |
|
||||
| `30311` | Live Event | [53](53.md) |
|
||||
| `30315` | User Statuses | [38](38.md) |
|
||||
@@ -206,12 +205,12 @@ Please update these lists when proposing NIPs introducing new event kinds.
|
||||
| `l` | label, label namespace | annotations | [32](32.md) |
|
||||
| `L` | label namespace | -- | [32](32.md) |
|
||||
| `m` | MIME type | -- | [94](94.md) |
|
||||
| `q` | event id (hex) | relay URL | [18](18.md) |
|
||||
| `r` | a reference (URL, etc) | petname | |
|
||||
| `r` | relay url | marker | [65](65.md) |
|
||||
| `t` | hashtag | -- | |
|
||||
| `alt` | summary | -- | [31](31.md) |
|
||||
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
|
||||
| `auth-required` | either nip-42 or nip-98 | -- | [302](302.md) |
|
||||
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
|
||||
| `challenge` | challenge string | -- | [42](42.md) |
|
||||
| `client` | name, address | relay URL | [89](89.md) |
|
||||
@@ -263,10 +262,6 @@ 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.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
[Breaking Changes](BREAKING.md)
|
||||
|
||||
## License
|
||||
|
||||
All NIPs are public domain.
|
||||
|
||||
Reference in New Issue
Block a user