├── .github ├── dependabot.yml └── workflows │ ├── lint.yml │ └── sync.yml ├── .gitignore ├── .husky └── pre-commit ├── .textlintignore ├── .textlintrc.json ├── 01.md ├── 02.md ├── 03.md ├── 04.md ├── 05.md ├── 06.md ├── 07.md ├── 08.md ├── 09.md ├── 10.md ├── 11.md ├── 12.md ├── 13.md ├── 14.md ├── 15.md ├── 16.md ├── 17.md ├── 18.md ├── 19.md ├── 20.md ├── 21.md ├── 22.md ├── 23.md ├── 24.md ├── 25.md ├── 26.md ├── 27.md ├── 28.md ├── 29.md ├── 30.md ├── 31.md ├── 32.md ├── 33.md ├── 34.md ├── 35.md ├── 36.md ├── 38.md ├── 39.md ├── 40.md ├── 42.md ├── 44.md ├── 45.md ├── 46.md ├── 47.md ├── 48.md ├── 49.md ├── 50.md ├── 51.md ├── 52.md ├── 53.md ├── 54.md ├── 55.md ├── 56.md ├── 57.md ├── 58.md ├── 59.md ├── 60.md ├── 61.md ├── 64.md ├── 65.md ├── 68.md ├── 69.md ├── 70.md ├── 71.md ├── 72.md ├── 73.md ├── 75.md ├── 78.md ├── 7D.md ├── 84.md ├── 86.md ├── 89.md ├── 90.md ├── 92.md ├── 94.md ├── 96.md ├── 98.md ├── 99.md ├── BREAKING.md ├── C7.md ├── CONTRIBUTING.md ├── README.md ├── package-lock.json └── package.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request_target: 5 | 6 | permissions: 7 | pull-requests: write 8 | 9 | jobs: 10 | textlint: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 10 13 | steps: 14 | # プルリクエスト元リポジトリからプルリクエスト作成時のプルリクエスト先baseブランチの最新コミットを取得 15 | - uses: actions/checkout@v4 16 | with: 17 | repository: ${{ github.event.pull_request.head.repo.full_name }} 18 | ref: ${{ github.event.pull_request.base.sha }} 19 | # プルリクエスト元リポジトリからプルリクエスト用ブランチの最新コミットを取得して上書き 20 | - uses: actions/checkout@v4 21 | with: 22 | repository: ${{ github.event.pull_request.head.repo.full_name }} 23 | ref: ${{ github.head_ref }} 24 | # プルリクエスト作成時のプルリクエスト先baseブランチの最新コミットとプルリクエスト先headブランチの最新コミットを比較 25 | - name: modified files 26 | run: | 27 | modified_files=$(git diff --name-only --diff-filter=AM ${{ github.event.pull_request.base.sha }}..origin/${{ github.head_ref }} | tr '\n' ' ') 28 | echo "textlint_flags=$modified_files" >> $GITHUB_ENV 29 | - uses: tsuyoshicho/action-textlint@v3 30 | with: 31 | fail_on_error: true 32 | textlint_flags: ${{ env.textlint_flags }} 33 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * 5" 7 | 8 | permissions: {} 9 | 10 | jobs: 11 | sync: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | steps: 15 | - uses: actions/create-github-app-token@v2 16 | id: create_token 17 | with: 18 | app-id: ${{ secrets.APP_ID }} 19 | private-key: ${{ secrets.PRIVATE_KEY }} 20 | 21 | - uses: actions/checkout@v4 22 | with: 23 | ref: main 24 | fetch-depth: 0 25 | token: ${{ steps.create_token.outputs.token }} 26 | 27 | - name: Sync with upstream 28 | id: sync 29 | run: | 30 | echo ::group::Setting up git and gh 31 | git config user.name "github-actions[bot]" 32 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 33 | gh repo set-default ${{ github.repository }} 34 | echo ::endgroup:: 35 | 36 | echo ::group::Fetching upstream 37 | git remote add upstream https://github.com/nostr-protocol/nips 38 | git fetch upstream 39 | echo ::endgroup:: 40 | 41 | echo ::group::Fetching origin 42 | git fetch origin 43 | echo ::endgroup:: 44 | 45 | echo ::group::Switching to sync branch 46 | git switch -c sync 47 | git log -1 --format=%H 48 | echo ::endgroup:: 49 | 50 | echo ::group::Merging upstream to sync branch 51 | if ! git merge upstream/master --no-edit --commit -m "Sync with upstream"; then 52 | git add -A 53 | git commit -m "Sync with upstream including CONFLICT" 54 | fi 55 | echo ::endgroup:: 56 | 57 | if ! git diff --quiet main; then 58 | echo "diff_main=1" >> $GITHUB_OUTPUT 59 | 60 | echo ::group::Pushing sync branch 61 | git push -f origin sync 62 | echo ::endgroup:: 63 | else 64 | echo "diff_main=0" >> $GITHUB_OUTPUT 65 | echo "main is in sync with upstream" 66 | fi 67 | env: 68 | GITHUB_TOKEN: ${{ steps.create_token.outputs.token }} 69 | 70 | - name: Check if sync pull request exists 71 | id: sync_pr_existence 72 | run: | 73 | echo "count=$(gh pr list --head sync --base main | wc -l)" >> $GITHUB_OUTPUT 74 | env: 75 | GITHUB_TOKEN: ${{ steps.create_token.outputs.token }} 76 | 77 | - name: Create pull request if needed 78 | if: ${{ steps.sync_pr_existence.outputs.count == 0 && steps.sync.outputs.diff_main == 1 }} 79 | run: | 80 | gh pr create --head "sync" --base "main" --title "Sync with upstream" --body "" 81 | env: 82 | GITHUB_TOKEN: ${{ steps.create_token.outputs.token }} 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run lint-staged 5 | -------------------------------------------------------------------------------- /.textlintignore: -------------------------------------------------------------------------------- 1 | # Ignore file: 2 | CONTRIBUTING.md 3 | -------------------------------------------------------------------------------- /.textlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "filters": {}, 3 | "plugins": {}, 4 | "rules": { 5 | "preset-ja-spacing": { 6 | "ja-no-space-around-parentheses": false 7 | }, 8 | "textlint-rule-preset-ja-technical-writing": { 9 | "ja-no-mixed-period": { 10 | "allowPeriodMarks": [ 11 | ":" 12 | ] 13 | }, 14 | "ja-no-redundant-expression": { 15 | "dictOptions": { 16 | "dict5": { 17 | "allows": [ 18 | "/^要求を行[ぁ-ん]/", 19 | "/^処理を行[ぁ-ん]/", 20 | "/^[ァ-ヶ]+を.?行[ぁ-ん]/", 21 | "/^[a-zA-Z]+を.?行[ぁ-ん]/" 22 | ] 23 | } 24 | } 25 | }, 26 | "ja-no-weak-phrase": false, 27 | "max-comma": false, 28 | "no-exclamation-question-mark": false, 29 | "no-mix-dearu-desumasu": { 30 | "preferInBody": "である", 31 | "preferInHeader": "", 32 | "preferInList": "である", 33 | "strict": true 34 | }, 35 | "sentence-length": false 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /02.md: -------------------------------------------------------------------------------- 1 | NIP-02 2 | ====== 3 | 4 | フォローリスト 5 | ----------- 6 | 7 | `final` `optional` 8 | 9 | 「フォローリスト」を意味するkind `3`の特別なイベントは、フォローしている/既知のプロフィールごとの`p`タグのリストを持つものとして定義される。 10 | 11 | タグの個々の要素は、プロフィールの鍵・その鍵からのイベントを発見できるリレーのURL (必要なければ空文字列で設定可能) ・そのプロフィールのローカル名 (あるいは「愛称」) (空文字列が設定されるか、提供されないようできる) を含むべきで、つまり`["p", <32-bytes hex key>,
, ]`である。 12 | 13 | `.content`は使用されない。 14 | 15 | 例: 16 | 17 | ```jsonc 18 | { 19 | "kind": 3, 20 | "tags": [ 21 | ["p", "91cf9..4e5ca", "wss://alicerelay.com/", "alice"], 22 | ["p", "14aeb..8dad4", "wss://bobrelay.com/nostr", "bob"], 23 | ["p", "612ae..e610f", "ws://carolrelay.com/ws", "carol"] 24 | ], 25 | "content": "", 26 | // other fields... 27 | } 28 | ``` 29 | 30 | 新しいフォローリストが公開されるたびに過去のものは上書きされるので、すべての要素を含むべきである。リレーとクライアントは新しいものを受け取ったら速やかに過去のフォローリストを削除すべきである (SHOULD) 。 31 | 32 | 既存のリストに新しいフォローを追加する際は常に、クライアントはそのユーザをリストの末尾に追加し、フォローした順に保存すべきである (SHOULD) 。 33 | 34 | ## 用途 35 | 36 | ### フォローリストのバックアップ 37 | 38 | リレーがイベントを十分な期間保存すると信じているなら、このkind 3イベントを使ってフォローリストをバックアップし、別のデバイスで復元できる。 39 | 40 | ### プロフィールの発見とコンテキストの拡張 41 | 42 | クライアントは、ブラウジングしているプロフィールがフォローしている人のリストを表示したり、フォローまたはブラウジングしている人のフォローリストに基づいて誰をフォローするかの提案リストを作成したり、他のコンテキストでデータを表示したりするためにkind 3イベントを頼りにしてもかまわない。 43 | 44 | ### リレーの共有 45 | 46 | クライアントは連絡先の完全なリストともにそれぞれの連絡先のために適したリレーを公開してもよく、これによりほかのクライアントは必要に応じてこれを内部のリレーリストの更新に使うことができ、検閲耐性が高まる。 47 | 48 | ### 愛称スキーム 49 | 50 | フォローリストからのデータは、クライアントによってほかの人々のフォローリストから派生したローカルの["petname"](http://www.skyhunter.com/marcs/petnames/IntroPetNames.html)テーブルを構築するために用いることができる。これにより、グローバルな人間が読める名前の必要性が軽減される。例えば: 51 | 52 | ユーザーは次のような内部のフォローリストを持っている。 53 | 54 | ```json 55 | [ 56 | ["p", "21df6d143fb96c2ec9d63726bf9edc71", "", "erin"] 57 | ] 58 | ``` 59 | 60 | そして、次のように2つのフォローリストを受け取り、1つは`21df6d143fb96c2ec9d63726bf9edc71`からである。 61 | 62 | ```json 63 | [ 64 | ["p", "a8bb3d884d5d90b413d9891fe4c4e46d", "", "david"] 65 | ] 66 | ``` 67 | 68 | もう1つは`a8bb3d884d5d90b413d9891fe4c4e46d`からである。 69 | 70 | ```json 71 | [ 72 | ["p", "f57f54057d2a7af0efecc8b0b66f5708", "", "frank"] 73 | ] 74 | ``` 75 | 76 | ユーザーが`21df6d143fb96c2ec9d63726bf9edc71`を見るとき、クライアントは代わりに _erin_ を表示できる。 77 | ユーザーが`a8bb3d884d5d90b413d9891fe4c4e46d`を見るとき、クライアントは代わりに _david.erin_ を表示できる。 78 | ユーザーが`f57f54057d2a7af0efecc8b0b66f5708`を見るとき、クライアントは代わりに _frank.david.erin_ を表示できる。 79 | -------------------------------------------------------------------------------- /03.md: -------------------------------------------------------------------------------- 1 | NIP-03 2 | ====== 3 | 4 | イベントに対するOpenTimestamps認証 5 | -------------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | このNIPは`kind:1040`のイベントを定義する。これは他のイベントに対する[OpenTimestamps](https://opentimestamps.org/)証明を含むことができる。 10 | 11 | ```json 12 | { 13 | "kind": 1040 14 | "tags": [ 15 | ["e", , ], 16 | ["alt", "opentimestamps attestation"] 17 | ], 18 | "content": 19 | } 20 | ``` 21 | 22 | - OpenTimestamps証明は、`e`タグで参照されているイベントを、そのIDをダイジェストとして証明しなければならない(MUST)。 23 | - `content`は、少なくとも1つのビットコイン認証を含む`.ots`ファイルの全内容でなければならない (MUST)。このファイルは**単一の**ビットコイン認証のみを含み (有効な認証は1つ以下の必要があり、かつバイト数は多いより少ない方が良いため) 、かつ「未解決」の認証に対する参照を持つべきではない(SHOULD)。なぜなら、それらはこの状況では役に立たないからだ。 24 | 25 | ### OpenTimestamps証明の検証フローの例 26 | 27 | [`nak`](https://github.com/fiatjaf/nak)、[`jq`](https://jqlang.github.io/jq/)、そして[`ots`](https://github.com/fiatjaf/ots)コマンドを用いる。 28 | 29 | ```bash 30 | ~> nak req -i e71c6ea722987debdb60f81f9ea4f604b5ac0664120dd64fb9d23abc4ec7c323 wss://nostr-pub.wellorder.net | jq -r .content | ots verify 31 | > using an esplora server at https://blockstream.info/api 32 | - sequence ending on block 810391 is valid 33 | timestamp validated at block [810391] 34 | ``` 35 | -------------------------------------------------------------------------------- /04.md: -------------------------------------------------------------------------------- 1 | > __Warning__ `unrecommended`: deprecated in favor of [NIP-17](17.md) 2 | 3 | NIP-04 4 | ====== 5 | 6 | 暗号化されたダイレクトメッセージ 7 | ------------------------ 8 | 9 | `final` `unrecommended` `optional` 10 | 11 | 暗号化されたダイレクトメッセージを指す固有のkind`4`は、次のような属性を持っている。 12 | 13 | **`content`** は、ユーザーが書きたい文字列のbase64エンコードを送信者の秘密鍵と受診者の公開鍵を組みあわせて作られる共有暗号で暗号化したものと等しくなければならない (MUST)。base64エンコードされた初期化ベクトルは、"iv"という名前のクエリ文字列パラメータのようにして添付される。ここに例を示す。`"content": "?iv="` 14 | 15 | **`tags`** にはリレーがうまく彼らにそのイベントを転送するため、メッセージの受信者を識別するエントリを次のように格納しなけばならない (MUST)。 `["p", ""]` 16 | 17 | **`tags`** には、文脈に沿った、より整理された会話ができるよう会話の中の前のメッセージや、明示的に返信しているメッセージを示すエントリを次のように含めても構わない (MAY)。`["e", ""]` 18 | 19 | **メモ**: ECDH実装[libsecp256k1](https://github.com/bitcoin-core/secp256k1)のデフォルトでは、秘密は共有点 (X座標とY座標の両方) のSHA256ハッシュである。Nostrでは、共有点のX座標のみが秘密として使用されハッシュ化されない。libsecp256k1を使用する場合は、X座標をコピーする関数を`secp256k1_ecdh`中で`hashfp`引数として渡す必要が有る。 20 | 21 | JavaScriptでそのようなイベントを生成するためのサンプルコードはこちら。 22 | 23 | ```js 24 | import crypto from 'crypto' 25 | import * as secp from '@noble/secp256k1' 26 | 27 | let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey) 28 | let sharedX = sharedPoint.slice(1, 33) 29 | 30 | let iv = crypto.randomFillSync(new Uint8Array(16)) 31 | var cipher = crypto.createCipheriv( 32 | 'aes-256-cbc', 33 | Buffer.from(sharedX), 34 | iv 35 | ) 36 | let encryptedMessage = cipher.update(text, 'utf8', 'base64') 37 | encryptedMessage += cipher.final('base64') 38 | let ivBase64 = Buffer.from(iv.buffer).toString('base64') 39 | 40 | let event = { 41 | pubkey: ourPubKey, 42 | created_at: Math.floor(Date.now() / 1000), 43 | kind: 4, 44 | tags: [['p', theirPublicKey]], 45 | content: encryptedMessage + '?iv=' + ivBase64 46 | } 47 | ``` 48 | 49 | ## セキュリティ上の警告 50 | 51 | この標準はピア間の暗号化通信の最先端と考えられている物とは程遠く、加えてイベントのメタデータも流出させる。そのため本当に秘密を保ちたい用途には絶対に使用してはならない。使用する際は、`AUTH`を使用してあなたの`kind:4`イベントを取得可能な人を制限できるリレーでのみ使用するべきである。 52 | 53 | ## クライアント実装の警告 54 | 55 | クライアントは`.content`に含まれる公開鍵とノートの参照を検索して置換*するべきではない*。もし通常のテキストノートのように処理 (文中の`@npub...`を`["p", "..."]`タグがある`#[0]`で置き換える) した場合、タグは流出し、メンションされたユーザーは受信トレイにメッセージを受け取る。 56 | -------------------------------------------------------------------------------- /05.md: -------------------------------------------------------------------------------- 1 | NIP-05 2 | ====== 3 | 4 | Nostr鍵をDNSベースのインターネット識別子に結びつける 5 | ---------------------------------------------------- 6 | 7 | `final` `optional` 8 | 9 | kind `0` (`user metadata`)には[インターネット識別子](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) (eメールのようなアドレス) を値として`"nip05"`というキーを指定できる。"インターネット識別子"に対する多種多様な仕様のリンクがあるが、NIP-05は``部分が`a-z0-9-_.`で大文字と小文字を区別しないことを前提としている。 10 | 11 | クライアントは識別子を``と``に分けた上でそれらの値で`https:///.well-known/nostr.json?name=`にGET要求を行う。 12 | 13 | 結果として、16進数で表される公開鍵に対応した`"names"`キーを持つ、JSONドキュメントオブジェクトが返されなければならない。指定された``の公開鍵が`user metadata`イベントの`pubkey`と一致する場合、クライアントは与えられた公開鍵が実際にその識別子で照会できると結論付ける。 14 | 15 | ### 例 16 | 17 | 18 | クライアントが下記のようなイベントを見た時。 19 | 20 | ```jsonc 21 | { 22 | "pubkey": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9", 23 | "kind": 0, 24 | "content": "{\"name\": \"bob\", \"nip05\": \"bob@example.com\"}" 25 | // other fields... 26 | } 27 | ``` 28 | 29 | `https://example.com/.well-known/nostr.json?name=bob`のようにGET要求を行い、次のような応答を得る。 30 | 31 | ```json 32 | { 33 | "names": { 34 | "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" 35 | } 36 | } 37 | ``` 38 | 39 | あるいは、**推奨**される`"relays"`属性が追加される場合は次の通り。 40 | 41 | ```json 42 | { 43 | "names": { 44 | "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" 45 | }, 46 | "relays": { 47 | "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ] 48 | } 49 | } 50 | ``` 51 | 52 | 上記の例のように公開鍵が`"names"`で与えられたものと一致する場合、関連付けが正しいことを意味し、`"nip05"`識別子は有効なものとして表示できる。 53 | 54 | 推奨される`"relays"`属性には、公開鍵をプロパティ名、リレーURLの配列を値に持つオブジェクトを含められる。存在する場合、クライアントが特定のユーザーを見つけられるリレーを知るために使用可能である。クエリ文字列に基づいて動的に`/.well-known/nostr.json`ファイルを提供するWebサーバーは、利用可能な場合、同じ応答で提供する名前に於けるリレーデータも提供すべきである (SHOULD)。 55 | 56 | ## NIP-05識別子からユーザーを見つける 57 | 58 | クライアントは_インターネット識別子_からユーザーの公開鍵を見つける機能を実装しても良い。流れは上記の逆である。まず、クライアントは_well-known_URLを参照して、そこからユーザーの公開鍵を取得する。その後ユーザーのkind`0`の取得を試みて、`"nip05"`と合致するか確認する。 59 | 60 | ## メモ 61 | 62 | ### 識別する、検証はしない 63 | 64 | NIP-05はユーザーを_検証_するためではなく、連絡先の交換や検索を容易とするために_識別_することを目的としている。 65 | 例外は有名なドメインを所有する (企業等) あるいは関係する人 (プロジェクト等) であり、NIP-05をそのドメインとの関係、ひいてはその背後にある組織との関係を証明するものとして利用でき、信用の要素を得ることができる。 66 | 67 | ### ユーザー発見実装の提案 68 | 69 | クライアントはこれをユーザーが他のプロフィールの検索で使えるようにできる。クライアントが検索ボックスなどを持っている場合、そこに"bob@example.com"と入力でき、クライアントはそれを識別して適切なクエリで公開鍵を取得し、ユーザーに提案できる。 70 | 71 | ### クライアントはNIP-05アドレスでなく常に公開鍵をフォローする必要がある。 72 | 73 | 例えば、もし公開鍵`abc…def`をもった`bob@bob.com`を見つけて、ユーザーがそのプロフィールをフォローすることにした際、クライアントは`bob@bob.com`ではなく`abc…def`を第一参照先にする必要がある。何らかの理由で、将来`https://bob.com/.well-known/nostr.json?name=bob`が公開鍵`1d2...e3f`を返すようになった場合でもクライアントはフォロー済みユーザーリストで`abc...def`を置き換えてはならない。但し、そのユーザーの”bob@bob.com”は無効な`”nip05”`プロパティとなるため、表示を止めるべきである。 74 | 75 | ### 公開鍵は16進数形式の必要がある。 76 | 77 | 鍵は16進数の形式で返される必要がある。NIP-19`npub`形式はこのNIPではなく、クライアントUIでの表示にのみ使用される。 78 | 79 | ### ドメインのみを識別子として表示する 80 | 81 | クライアントは`_@domain`識別子を”ルート”識別子として扱い、``のみを表示できる。例えば、Bobが`bob.com`を所有している場合、`bob@bob.com`のような冗長な識別子を望まないかもしれない。代わりに、`_bob.com`を使用してNostrクライアントが如何なる目的でも単に`bob.com`として扱い、表示することを期待できる。 82 | 83 | ### `/.well-known/nostr.json?name=`書式の理由 84 | 85 | パスの代わりにクエリ文字列として``を追加することで、このプロトコルはオンデマンドでJSONを生成できる動的なサーバーと、複数の名前を含むJSONファイルを置く静的なサーバーの両方に対応できる。 86 | 87 | ### JavaScriptアプリへのアクセス許可 88 | 89 | JavaScript Nostrアプリはブラウザの[CORS][]ポリシーによってユーザーのドメインで`/.well-known/nostr.json`へのアクセスを妨げられる可能性がある。CORSがJSに資源を読み込ませなかった場合、JSプログラムはそれを存在しない資源と同じネットワーク障害と見做すため、バニラJSアプリが障害はCORSの問題で引き起こされたことを伝える方法は無い。JS Nostrアプリは`/.well-known/nostr.json`ファイルを要求した時ネットワーク障害に遭遇したら、サーバのCORSポリシーを確認することをユーザに勧めた方がいいかもしれない。 90 | 91 | ```bash 92 | $ curl -sI https://example.com/.well-known/nostr.json?name=bob | grep -i ^Access-Control 93 | Access-Control-Allow-Origin: * 94 | ``` 95 | 96 | ユーザーは最新のブラウザで実行されるバニラJSアプリが検証できるよう、`/.well-known/nostr.json`を`Access-Control-Allow-Origin: *`のHTTPヘッダ付きで提供しなければならない。 97 | 98 | [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS 99 | 100 | ### セキュリティ上の制約 101 | 102 | /.well-known/nostr.json`のエンドポイントはHTTPリダイレクトを返してはならない (MUST)。 103 | 104 | 取得する側は`/.well-known/nostr.json`からのHTTPリダイレクトを無視しなければならない (MUST)。 105 | -------------------------------------------------------------------------------- /06.md: -------------------------------------------------------------------------------- 1 | NIP-06 2 | ====== 3 | 4 | ニーモニックシードフレーズからの基本的な鍵導出 5 | ---------------------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | [BIP39](https://bips.xyz/39) はニーモニックシードワードを生成し、そこからバイナリシードを導出するために使用される。 10 | 11 | [BIP32](https://bips.xyz/32) はパス`m/44'/1237'/'/0/0`を導出するために使用される。 ([SLIP44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md)のNostrエントリを参照。) 12 | 13 | 基本的なクライアントは、単純に`0`の`account`を使用して単一の鍵を導出できる。より高度な使用例では、`account`をインクリメントさせることでhardeningされた導出による5レベルパスから実質的に無限の鍵を生成できる。 14 | 15 | 他のタイプのクライアントは別の目的のために異なる導出パスを使用できる。 16 | 17 | ### テストベクタ 18 | 19 | ニーモニック: leader monkey parrot ring guide accident before fence cannon height naive bean\ 20 | 秘密鍵 (16進数): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a\ 21 | nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp\ 22 | 公開鍵 (16進数): 17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917\ 23 | npub: npub1zutzeysacnf9rru6zqwmxd54mud0k44tst6l70ja5mhv8jjumytsd2x7nu 24 | 25 | --- 26 | 27 | ニーモニック: what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade\ 28 | 秘密鍵 (16進数): c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add\ 29 | nsec: nsec1c9wh8xy5eqdzln7n5t0ctgxjcrdug73gp5yj0x03gntn67h83twssdfhel\ 30 | 公開鍵 (16進数): d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573\ 31 | npub: npub16sdj9zv4f8sl85e45vgq9n7nsgt5qphpvmf7vk8r5hhvmdjxx4es8rq74h 32 | -------------------------------------------------------------------------------- /07.md: -------------------------------------------------------------------------------- 1 | NIP-07 2 | ====== 3 | 4 | Webブラウザ向け`window.nostr`機能 5 | ------------------------------------------ 6 | 7 | `draft` `optional` 8 | 9 | `window.nostr`オブジェクトはWebブラウザや拡張機能により作成される。WebサイトやWebアプリケーションは利用可能性を確認した上で、このオブジェクトを利用してもよい。 10 | 11 | このオブジェクトは下記のメソッドを定義せねばならない: 12 | 13 | ``` 14 | async window.nostr.getPublicKey(): string // returns a public key as hex 15 | async window.nostr.signEvent(event: { created_at: number, kind: number, tags: string[][], content: string }): Event // takes an event object, adds `id`, `pubkey` and `sig` and returns it 16 | ``` 17 | 18 | 上記2つのメソッドの他に、オプションとして下記のメソッドを定義してもよい。 19 | ``` 20 | async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies 21 | async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated) 22 | async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated) 23 | async window.nostr.nip44.encrypt(pubkey, plaintext): string // returns ciphertext as specified in nip-44 24 | async window.nostr.nip44.decrypt(pubkey, ciphertext): string // takes ciphertext as specified in nip-44 25 | ``` 26 | 27 | ### 拡張機能の作成者への推奨事項 28 | ページの読み込み時にnostrクライアントが`window.nostr`を利用できるようにするには、ChromiumおよびFirefoxの拡張機能の作成者は、拡張機能のマニフェストで`"run_at": "document_end"`を指定してスクリプトをロードする必要がある。 29 | 30 | 31 | ### 実装 32 | 33 | https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions を参照する。 34 | -------------------------------------------------------------------------------- /08.md: -------------------------------------------------------------------------------- 1 | > __Warning__ `unrecommended`: deprecated in favor of [NIP-27](27.md) 2 | 3 | NIP-08 4 | ====== 5 | 6 | Handling Mentions 7 | ----------------- 8 | 9 | `final` `unrecommended` `optional` 10 | 11 | This document standardizes the treatment given by clients of inline mentions of other events and pubkeys inside the content of `text_note`s. 12 | 13 | Clients that want to allow tagged mentions they MUST show an autocomplete component or something analogous to that whenever the user starts typing a special key (for example, "@") or presses some button to include a mention etc -- or these clients can come up with other ways to unambiguously differentiate between mentions and normal text. 14 | 15 | Once a mention is identified, for example, the pubkey `27866e9d854c78ae625b867eefdfa9580434bc3e675be08d2acb526610d96fbe`, the client MUST add that pubkey to the `.tags` with the tag `p`, then replace its textual reference (inside `.content`) with the notation `#[index]` in which "index" is equal to the 0-based index of the related tag in the tags array. 16 | 17 | The same process applies for mentioning event IDs. 18 | 19 | A client that receives a `text_note` event with such `#[index]` mentions in its `.content` CAN do a search-and-replace using the actual contents from the `.tags` array with the actual pubkey or event ID that is mentioned, doing any desired context augmentation (for example, linking to the pubkey or showing a preview of the mentioned event contents) it wants in the process. 20 | 21 | Where `#[index]` has an `index` that is outside the range of the tags array or points to a tag that is not an `e` or `p` tag or a tag otherwise declared to support this notation, the client MUST NOT perform such replacement or augmentation, but instead display it as normal text. 22 | -------------------------------------------------------------------------------- /09.md: -------------------------------------------------------------------------------- 1 | NIP-09 2 | ====== 3 | 4 | Event Deletion Request 5 | ---------------------- 6 | 7 | `draft` `optional` 8 | 9 | A special event with kind `5`, meaning "deletion request" is defined as having a list of one or more `e` or `a` tags, each referencing an event the author is requesting to be deleted. Deletion requests SHOULD include a `k` tag for the kind of each event being requested for deletion. 10 | 11 | The event's `content` field MAY contain a text note describing the reason for the deletion request. 12 | 13 | For example: 14 | 15 | ```jsonc 16 | { 17 | "kind": 5, 18 | "pubkey": <32-bytes hex-encoded public key of the event creator>, 19 | "tags": [ 20 | ["e", "dcd59..464a2"], 21 | ["e", "968c5..ad7a4"], 22 | ["a", "::"], 23 | ["k", "1"], 24 | ["k", "30023"] 25 | ], 26 | "content": "these posts were published by accident", 27 | // other fields... 28 | } 29 | ``` 30 | 31 | Relays SHOULD delete or stop publishing any referenced events that have an identical `pubkey` as the deletion request. Clients SHOULD hide or otherwise indicate a deletion request status for referenced events. 32 | 33 | Relays SHOULD continue to publish/share the deletion request events indefinitely, as clients may already have the event that's intended to be deleted. Additionally, clients SHOULD broadcast deletion request events to other relays which don't have it. 34 | 35 | When an `a` tag is used, relays SHOULD delete all versions of the replaceable event up to the `created_at` timestamp of the deletion request event. 36 | 37 | ## Client Usage 38 | 39 | Clients MAY choose to fully hide any events that are referenced by valid deletion request events. This includes text notes, direct messages, or other yet-to-be defined event kinds. Alternatively, they MAY show the event along with an icon or other indication that the author has "disowned" the event. The `content` field MAY also be used to replace the deleted events' own content, although a user interface should clearly indicate that this is a deletion request reason, not the original content. 40 | 41 | A client MUST validate that each event `pubkey` referenced in the `e` tag of the deletion request is identical to the deletion request `pubkey`, before hiding or deleting any event. Relays can not, in general, perform this validation and should not be treated as authoritative. 42 | 43 | Clients display the deletion request event itself in any way they choose, e.g., not at all, or with a prominent notice. 44 | 45 | Clients MAY choose to inform the user that their request for deletion does not guarantee deletion because it is impossible to delete events from all relays and clients. 46 | 47 | ## Relay Usage 48 | 49 | Relays MAY validate that a deletion request event only references events that have the same `pubkey` as the deletion request itself, however this is not required since relays may not have knowledge of all referenced events. 50 | 51 | ## Deletion Request of a Deletion Request 52 | 53 | Publishing a deletion request event against a deletion request has no effect. Clients and relays are not obliged to support "unrequest deletion" functionality. 54 | -------------------------------------------------------------------------------- /10.md: -------------------------------------------------------------------------------- 1 | NIP-10 2 | ====== 3 | 4 | 5 | On "e" and "p" tags in Text Events (kind 1) 6 | ------------------------------------------- 7 | 8 | `draft` `optional` 9 | 10 | ## Abstract 11 | This NIP describes how to use "e" and "p" tags in text events, especially those that are replies to other text events. It helps clients thread the replies into a tree rooted at the original event. 12 | 13 | ## Marked "e" tags (PREFERRED) 14 | `["e", , , , ]` 15 | 16 | Where: 17 | 18 | * `` is the id of the event being referenced. 19 | * `` is the URL of a recommended relay associated with the reference. Clients SHOULD add a valid `` field, but may instead leave it as `""`. 20 | * `` is optional and if present is one of `"reply"`, `"root"`, or `"mention"`. 21 | * `` is optional, SHOULD be the pubkey of the author of the referenced event 22 | 23 | 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. 24 | 25 | A direct reply to the root of a thread should have a single marked "e" tag of type "root". 26 | 27 | >This scheme is preferred because it allows events to mention others without confusing them with `` or ``. 28 | 29 | `` 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. 30 | 31 | ## The "p" tag 32 | Used in a text event contains a list of pubkeys used to record who is involved in a reply thread. 33 | 34 | When replying to a text event E the reply event's "p" tags should contain all of E's "p" tags as well as the `"pubkey"` of the event being replied to. 35 | 36 | Example: Given a text event authored by `a1` with "p" tags [`p1`, `p2`, `p3`] then the "p" tags of the reply should be [`a1`, `p1`, `p2`, `p3`] 37 | in no particular order. 38 | 39 | ## Deprecated Positional "e" tags 40 | 41 | This scheme is not in common use anymore and is here just to keep backward compatibility with older events on the network. 42 | 43 | Positional `e` tags are deprecated because they create ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply. 44 | 45 | They use simple `e` tags without any marker. 46 | 47 | `["e", , ]` as per NIP-01. 48 | 49 | Where: 50 | 51 | * `` is the id of the event being referenced. 52 | * `` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional. 53 | 54 | **The positions of the "e" tags within the event denote specific meanings as follows**: 55 | 56 | * No "e" tag:
57 | This event is not a reply to, nor does it refer to, any other event. 58 | 59 | * One "e" tag:
60 | `["e", ]`: The id of the event to which this event is a reply. 61 | 62 | * Two "e" tags: `["e", ]`, `["e", ]`
63 | `` is the id of the event at the root of the reply chain. `` is the id of the article to which this event is a reply. 64 | 65 | * Many "e" tags: `["e", ]` `["e", ]`, ..., `["e", ]`
66 | There may be any number of ``. These are the ids of events which may, or may not be in the reply chain. 67 | They are citing from this event. `root-id` and `reply-id` are as above. -------------------------------------------------------------------------------- /12.md: -------------------------------------------------------------------------------- 1 | NIP-12 2 | ====== 3 | 4 | Generic Tag Queries 5 | ------------------- 6 | 7 | `final` `mandatory` 8 | 9 | Moved to [NIP-01](01.md). 10 | -------------------------------------------------------------------------------- /13.md: -------------------------------------------------------------------------------- 1 | NIP-13 2 | ====== 3 | 4 | Proof of Work 5 | ------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines a way to generate and interpret Proof of Work for nostr notes. Proof of Work (PoW) is a way to add a proof of computational work to a note. This is a bearer proof that all relays and clients can universally validate with a small amount of code. This proof can be used as a means of spam deterrence. 10 | 11 | `difficulty` is defined to be the number of leading zero bits in the `NIP-01` id. For example, an id of `000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d` has a difficulty of `36` with `36` leading 0 bits. 12 | 13 | `002f...` is `0000 0000 0010 1111...` in binary, which has 10 leading zeroes. Do not forget to count leading zeroes for hex digits <= `7`. 14 | 15 | Mining 16 | ------ 17 | 18 | To generate PoW for a `NIP-01` note, a `nonce` tag is used: 19 | 20 | ```json 21 | {"content": "It's just me mining my own business", "tags": [["nonce", "1", "21"]]} 22 | ``` 23 | 24 | When mining, the second entry to the nonce tag is updated, and then the id is recalculated (see [NIP-01](./01.md)). If the id has the desired number of leading zero bits, the note has been mined. It is recommended to update the `created_at` as well during this process. 25 | 26 | The third entry to the nonce tag `SHOULD` contain the target difficulty. This allows clients to protect against situations where bulk spammers targeting a lower difficulty get lucky and match a higher difficulty. For example, if you require 40 bits to reply to your thread and see a committed target of 30, you can safely reject it even if the note has 40 bits difficulty. Without a committed target difficulty you could not reject it. Committing to a target difficulty is something all honest miners should be ok with, and clients `MAY` reject a note matching a target difficulty if it is missing a difficulty commitment. 27 | 28 | Example mined note 29 | ------------------ 30 | 31 | ```json 32 | { 33 | "id": "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358", 34 | "pubkey": "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", 35 | "created_at": 1651794653, 36 | "kind": 1, 37 | "tags": [ 38 | ["nonce", "776797", "20"] 39 | ], 40 | "content": "It's just me mining my own business", 41 | "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" 42 | } 43 | ``` 44 | 45 | Validating 46 | ---------- 47 | 48 | Here is some reference C code for calculating the difficulty (aka number of leading zero bits) in a nostr event id: 49 | 50 | ```c 51 | int zero_bits(unsigned char b) 52 | { 53 | int n = 0; 54 | 55 | if (b == 0) 56 | return 8; 57 | 58 | while (b >>= 1) 59 | n++; 60 | 61 | return 7-n; 62 | } 63 | 64 | /* find the number of leading zero bits in a hash */ 65 | int count_leading_zero_bits(unsigned char *hash) 66 | { 67 | int bits, total, i; 68 | for (i = 0, total = 0; i < 32; i++) { 69 | bits = zero_bits(hash[i]); 70 | total += bits; 71 | if (bits != 8) 72 | break; 73 | } 74 | return total; 75 | } 76 | ``` 77 | 78 | Here is some JavaScript code for doing the same thing: 79 | 80 | ```javascript 81 | // hex should be a hexadecimal string (with no 0x prefix) 82 | function countLeadingZeroes(hex) { 83 | let count = 0; 84 | 85 | for (let i = 0; i < hex.length; i++) { 86 | const nibble = parseInt(hex[i], 16); 87 | if (nibble === 0) { 88 | count += 4; 89 | } else { 90 | count += Math.clz32(nibble) - 28; 91 | break; 92 | } 93 | } 94 | 95 | return count; 96 | } 97 | ``` 98 | 99 | Delegated Proof of Work 100 | ----------------------- 101 | 102 | Since the `NIP-01` note id does not commit to any signature, PoW can be outsourced to PoW providers, perhaps for a fee. This provides a way for clients to get their messages out to PoW-restricted relays without having to do any work themselves, which is useful for energy-constrained devices like mobile phones. 103 | -------------------------------------------------------------------------------- /14.md: -------------------------------------------------------------------------------- 1 | NIP-14 2 | ====== 3 | 4 | Subject tag in Text events 5 | -------------------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines the use of the "subject" tag in text (kind: 1) events. 10 | (implemented in more-speech) 11 | 12 | ```json 13 | ["subject": ] 14 | ``` 15 | 16 | Browsers often display threaded lists of messages. The contents of the subject tag can be used in such lists, instead of the more ad hoc approach of using the first few words of the message. This is very similar to the way email browsers display lists of incoming emails by subject rather than by contents. 17 | 18 | When replying to a message with a subject, clients SHOULD replicate the subject tag. Clients MAY adorn the subject to denote 19 | that it is a reply. e.g. by prepending "Re:". 20 | 21 | Subjects should generally be shorter than 80 chars. Long subjects will likely be trimmed by clients. 22 | -------------------------------------------------------------------------------- /16.md: -------------------------------------------------------------------------------- 1 | NIP-16 2 | ====== 3 | 4 | Event Treatment 5 | --------------- 6 | 7 | `final` `mandatory` 8 | 9 | Moved to [NIP-01](01.md). 10 | -------------------------------------------------------------------------------- /18.md: -------------------------------------------------------------------------------- 1 | NIP-18 2 | ====== 3 | 4 | リポスト 5 | ------- 6 | 7 | `draft` `optional` 8 | 9 | リポストは`kind 1`テキスト投稿が読む価値を持つことを 10 | フォロワーへ知らせるために使われる`kind 6`イベントである。 11 | 12 | リポストイベントの`content`は _リポストされた投稿を文字列化したJSON_ だ。これは空でもかまわない (MAY) が、推奨されない。 13 | 14 | リポストイベントはリポストされた投稿の`id`を持つ`e`タグを含まなければならない (MUST) 。 15 | そのタグは投稿を取得可能な場所を指すリレーURLを 16 | 3番目の値として含まなければならない (MUST) 。 17 | 18 | リポストはリポストされたイベントの`pubkey`を持つ`p`タグを 19 | 含めるべきだ (SHOULD) 。 20 | 21 | ## 引用リポスト 22 | 23 | 引用リポストはリポストされた投稿の`q`タグが埋め込まれた`kind 1`イベントである。 24 | `q`タグは引用リポストがスレッドの返信として取り込まれないようにする。 25 | また、これで投稿への全ての引用を簡単に取得し、カウントできる。 26 | 27 | `q`タグは、`mark`引数の例外を除いて、NIP-10の`e`タグと同じ規則に従う必要がある。 28 | 29 | `["q", , , ]` 30 | 31 | 引用リポストには、コンテンツにイベントの [NIP-21](21.md) `nevent`、`note`、または `naddr` を含める必要がある。 32 | 33 | ## 汎用リポスト 34 | 35 | `kind 6`のリポストは`kind 1`のコンテンツのために予約されているため、`kind 16`を 36 | 「汎用リポスト」として用い、これは`kind 1`以外のあらゆる種類のイベントを含めることが 37 | できる。 38 | 39 | `kind 16`のリポストは、リポストされたイベントの文字列化されたkind番号を値とする 40 | `k`タグを含むべきだ (SHOULD) 。 41 | -------------------------------------------------------------------------------- /19.md: -------------------------------------------------------------------------------- 1 | NIP-19 2 | ====== 3 | 4 | bech32-encoded entities 5 | ----------------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP standardizes bech32-formatted strings that can be used to display keys, ids and other information in clients. These formats are not meant to be used anywhere in the core protocol, they are only meant for displaying to users, copy-pasting, sharing, rendering QR codes and inputting data. 10 | 11 | It is recommended that ids and keys are stored in either hex or binary format, since these formats are closer to what must actually be used the core protocol. 12 | 13 | ## Bare keys and ids 14 | 15 | To prevent confusion and mixing between private keys, public keys and event ids, which are all 32 byte strings. bech32-(not-m) encoding with different prefixes can be used for each of these entities. 16 | 17 | These are the possible bech32 prefixes: 18 | 19 | - `npub`: public keys 20 | - `nsec`: private keys 21 | - `note`: note ids 22 | 23 | Example: the hex public key `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` translates to `npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6`. 24 | 25 | The bech32 encodings of keys and ids are not meant to be used inside the standard NIP-01 event formats or inside the filters, they're meant for human-friendlier display and input only. Clients should still accept keys in both hex and npub format for now, and convert internally. 26 | 27 | ## Shareable identifiers with extra metadata 28 | 29 | When sharing a profile or an event, an app may decide to include relay information and other metadata such that other apps can locate and display these entities more easily. 30 | 31 | For these events, the contents are a binary-encoded list of `TLV` (type-length-value), with `T` and `L` being 1 byte each (`uint8`, i.e. a number in the range of 0-255), and `V` being a sequence of bytes of the size indicated by `L`. 32 | 33 | These are the possible bech32 prefixes with `TLV`: 34 | 35 | - `nprofile`: a nostr profile 36 | - `nevent`: a nostr event 37 | - `naddr`: a nostr _replaceable event_ coordinate 38 | - `nrelay`: a nostr relay (deprecated) 39 | 40 | These possible standardized `TLV` types are indicated here: 41 | 42 | - `0`: `special` 43 | - depends on the bech32 prefix: 44 | - for `nprofile` it will be the 32 bytes of the profile public key 45 | - for `nevent` it will be the 32 bytes of the event id 46 | - for `naddr`, it is the identifier (the `"d"` tag) of the event being referenced. For normal replaceable events use an empty string. 47 | - `1`: `relay` 48 | - for `nprofile`, `nevent` and `naddr`, _optionally_, a relay in which the entity (profile or event) is more likely to be found, encoded as ascii 49 | - this may be included multiple times 50 | - `2`: `author` 51 | - for `naddr`, the 32 bytes of the pubkey of the event 52 | - for `nevent`, _optionally_, the 32 bytes of the pubkey of the event 53 | - `3`: `kind` 54 | - for `naddr`, the 32-bit unsigned integer of the kind, big-endian 55 | - for `nevent`, _optionally_, the 32-bit unsigned integer of the kind, big-endian 56 | 57 | ## Examples 58 | 59 | - `npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg` should decode into the public key hex `7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e` and vice-versa 60 | - `nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5` should decode into the private key hex `67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa` and vice-versa 61 | - `nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` should decode into a profile with the following TLV items: 62 | - pubkey: `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` 63 | - relay: `wss://r.x.com` 64 | - relay: `wss://djbas.sadkb.com` 65 | 66 | ## Notes 67 | 68 | - `npub` keys MUST NOT be used in NIP-01 events or in NIP-05 JSON responses, only the hex format is supported there. 69 | - When decoding a bech32-formatted string, TLVs that are not recognized or supported should be ignored, rather than causing an error. 70 | -------------------------------------------------------------------------------- /20.md: -------------------------------------------------------------------------------- 1 | NIP-20 2 | ====== 3 | 4 | Command Results 5 | --------------- 6 | 7 | `final` `mandatory` 8 | 9 | Moved to [NIP-01](01.md). 10 | -------------------------------------------------------------------------------- /21.md: -------------------------------------------------------------------------------- 1 | NIP-21 2 | ====== 3 | 4 | `nostr:` URIスキーム 5 | ------------------- 6 | 7 | `draft` `optional` 8 | 9 | このNIPは、ネットワークの相互運用性とオープンネスを最大限に高めるため、共通のURIスキームの使用を標準化する。 10 | 11 | スキームは `nostr:` である。 12 | 13 | 後に続く識別子は、[NIP-19](19.md)で定義されているものと同じであることが期待される(`nsec` を除く)。 14 | 15 | ## 例 16 | 17 | - `nostr:npub1sn0wdenkukak0d9dfczzeacvhkrgz92ak56egt7vdgzn8pv2wfqqhrjdv9` 18 | - `nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` 19 | - `nostr:note1fntxtkcy9pjwucqwa9mddn7v03wwwsu9j330jj350nvhpky2tuaspk6nqc` 20 | - `nostr:nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm` 21 | -------------------------------------------------------------------------------- /22.md: -------------------------------------------------------------------------------- 1 | NIP-22 2 | ====== 3 | 4 | Comment 5 | ------- 6 | 7 | `draft` `optional` 8 | 9 | A comment is a threading note always scoped to a root event or an `I`-tag. 10 | 11 | It uses `kind:1111` with plaintext `.content` (no HTML, Markdown, or other formatting). 12 | 13 | Comments MUST point to the root scope using uppercase tag names (e.g. `K`, `E`, `A` or `I`) 14 | and MUST point to the parent item with lowercase ones (e.g. `k`, `e`, `a` or `i`). 15 | 16 | ```jsonc 17 | { 18 | kind: 1111, 19 | content: '', 20 | tags: [ 21 | // root scope: event addresses, event ids, or I-tags. 22 | ["", "", "", ""], 23 | // the root item kind 24 | ["K", ""], 25 | 26 | // parent item: event addresses, event ids, or i-tags. 27 | ["", "", "", ""], 28 | // parent item kind 29 | ["k", ""] 30 | ] 31 | // other fields 32 | } 33 | ``` 34 | 35 | Tags `K` and `k` MUST be present to define the event kind of the root and the parent items. 36 | 37 | `I` and `i` tags create scopes for hashtags, geohashes, URLs, and other external identifiers. 38 | 39 | The possible values for `i` tags – and `k` tags, when related to an extenal identity – are listed on [NIP-73](73.md). 40 | Their uppercase versions use the same type of values but relate to the root item instead of the parent one. 41 | 42 | `q` tags MAY be used when citing events in the `.content` with [NIP-21](21.md). 43 | 44 | ```json 45 | ["q", " or ", "", ""] 46 | ``` 47 | 48 | `p` tags SHOULD be used when mentioning pubkeys in the `.content` with [NIP-21](21.md). 49 | If the parent item is an event, a `p` tag set to the parent event's author SHOULD be added. 50 | 51 | ```json 52 | ["p", "", ""] 53 | ``` 54 | 55 | ## Examples 56 | 57 | A comment on a blog post looks like this: 58 | 59 | ```jsonc 60 | { 61 | kind: 1111, 62 | content: 'Great blog post!', 63 | tags: [ 64 | // top-level comments scope to event addresses or ids 65 | ["A", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"], 66 | // the root kind 67 | ["K", "30023"], 68 | 69 | // the parent event address (same as root for top-level comments) 70 | ["a", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"], 71 | // when the parent event is replaceable or addressable, also include an `e` tag referencing its id 72 | ["e", "5b4fc7fed15672fefe65d2426f67197b71ccc82aa0cc8a9e94f683eb78e07651", "wss://example.relay"], 73 | // the parent event kind 74 | ["k", "30023"] 75 | ] 76 | // other fields 77 | } 78 | ``` 79 | 80 | A comment on a [NIP-94](94.md) file looks like this: 81 | 82 | ```jsonc 83 | { 84 | kind: 1111, 85 | content: 'Great file!', 86 | tags: [ 87 | // top-level comments have the same scope and reply to addresses or ids 88 | ["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"], 89 | // the root kind 90 | ["K", "1063"], 91 | 92 | // the parent event id (same as root for top-level comments) 93 | ["e", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"], 94 | // the parent kind 95 | ["k", "1063"] 96 | ] 97 | // other fields 98 | } 99 | ``` 100 | 101 | A reply to a comment looks like this: 102 | 103 | ```jsonc 104 | { 105 | kind: 1111, 106 | content: 'This is a reply to "Great file!"', 107 | tags: [ 108 | // nip-94 file event id 109 | ["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "fd913cd6fa9edb8405750cd02a8bbe16e158b8676c0e69fdc27436cc4a54cc9a"], 110 | // the root kind 111 | ["K", "1063"], 112 | 113 | // the parent event 114 | ["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://example.relay", "93ef2ebaaf9554661f33e79949007900bbc535d239a4c801c33a4d67d3e7f546"], 115 | // the parent kind 116 | ["k", "1111"] 117 | ] 118 | // other fields 119 | } 120 | ``` 121 | 122 | A comment on a website's url looks like this: 123 | 124 | ```jsonc 125 | { 126 | kind: 1111, 127 | content: 'Nice article!', 128 | tags: [ 129 | // referencing the root url 130 | ["I", "https://abc.com/articles/1"], 131 | // the root "kind": for an url, the kind is its domain 132 | ["K", "https://abc.com"], 133 | 134 | // the parent reference (same as root for top-level comments) 135 | ["i", "https://abc.com/articles/1"], 136 | // the parent "kind": for an url, the kind is its domain 137 | ["k", "https://abc.com"] 138 | ] 139 | // other fields 140 | } 141 | ``` 142 | 143 | A podcast comment example: 144 | 145 | ```jsonc 146 | { 147 | id: "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05", 148 | pubkey: "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111", 149 | kind: 1111, 150 | content: "This was a great episode!", 151 | tags: [ 152 | // podcast episode reference 153 | ["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"], 154 | // podcast episode type 155 | ["K", "podcast:item:guid"], 156 | 157 | // same value as "I" tag above, because it is a top-level comment (not a reply to a comment) 158 | ["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"], 159 | ["k", "podcast:item:guid"] 160 | ] 161 | // other fields 162 | } 163 | ``` 164 | 165 | A reply to a podcast comment: 166 | 167 | ```jsonc 168 | { 169 | kind: 1111, 170 | content: "I'm replying to the above comment.", 171 | tags: [ 172 | // podcast episode reference 173 | ["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"], 174 | // podcast episode type 175 | ["K", "podcast:item:guid"], 176 | 177 | // this is a reference to the above comment 178 | ["e", "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05", "wss://example.relay", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111"], 179 | // the parent comment kind 180 | ["k", "1111"] 181 | ] 182 | // other fields 183 | } 184 | ``` 185 | -------------------------------------------------------------------------------- /23.md: -------------------------------------------------------------------------------- 1 | NIP-23 2 | ====== 3 | 4 | 長文投稿 5 | ----------------- 6 | 7 | `draft` `optional` 8 | 9 | このNIPでは`kind:30023` (アドレス指定可能 (addressable) なイベント) で長文投稿、一般的に「記事」や「ブログ投稿」と呼ばれるものを定義する。`kind:30024`は`kind:30023`と同じ構造を持ち、下書きを保存するために用いられる。 10 | 11 | `kind:1`を主に取り扱うソーシャルクライアント (マイクロブログ系クライアント) に、このNIPの実装は強制されない。 12 | 13 | ### フォーマット 14 | 15 | 長文投稿の`.content`は、Markdown構文の文字列テキストでなければならない。異なるクライアントやデバイス間の互換性と可読性を最大化するために、長文投稿を作成する各クライアントは以下の制約に従う必要がある。 16 | 17 | - 80カラム境界で改行するといった様な、段落の強制改行をしてはならない (MUST NOT) 。 18 | 19 | - MarkdownへのHTMLの追加をサポートしてはならない (MUST NOT)。 20 | 21 | ### メタデータ 22 | 23 | 最終更新日については`.created_at`フィールドを使用し、タグ/ハッシュタグ、(つまり、その記事が関連するトピック) については`t`タグを含めることが望ましい。 24 | 25 | その他メタデータフィールドは、必要に応じてイベントにタグを追加できる。ここでは有用と思われる4つのタグを標準化する(厳密に任意)。 26 | 27 | - `"title"`, 記事のタイトル 28 | - `"image"`, トップ画像のURL 29 | - `"summary"`, 記事の要約 30 | - `"published_at"`, 初版公開時刻の(文字列化された)UNIX秒タイムスタンプ 31 | 32 | ### 編集可能性 33 | 34 | 長文投稿は編集する可能性を意図しているため、記事の識別子を持つ`d`タグを含める必要がある。各クライアントは、`d`タグを実装したイベントのみをリレーに発行し、読み取るように注意すべきだ。また同じ記事の古い版を非表示にすることにも注意すべきだ。 35 | 36 | ### リンク 37 | 38 | [NIP-19](19.md)で定義された`naddr`コード (`a`タグを含む) を使って記事をリンクできる。 39 | 40 | ### 参照 41 | 42 | 他の投稿・記事・プロフィールへの参照は[NIP-27](27.md)に沿って行わなければならない。つまり、[NIP-21](21.md)で定義された`nostr:…`リンクを使用し、また任意でそのリンクに対応したタグを追加する (下記イベント例を参照) 。 43 | 44 | ## イベント例 45 | 46 | ```json 47 | { 48 | "kind": 30023, 49 | "created_at": 1675642635, 50 | "content": "Lorem [ipsum][nostr:nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9] dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nRead more at nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu.", 51 | "tags": [ 52 | ["d", "lorem-ipsum"], 53 | ["title", "Lorem Ipsum"], 54 | ["published_at", "1296962229"], 55 | ["t", "placeholder"], 56 | ["e", "b3e392b11f5d4f28321cedd09303a748acfd0487aea5a7450b3481c60b6e4f87", "wss://relay.example.com"], 57 | ["a", "30023:a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919:ipsum", "wss://relay.nostr.org"] 58 | ], 59 | "pubkey": "...", 60 | "id": "..." 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /24.md: -------------------------------------------------------------------------------- 1 | NIP-24 2 | ====== 3 | 4 | 追加のメタデータフィールドとタグ 5 | ------------------------------ 6 | 7 | `draft` `optional` 8 | 9 | このNIPは、どこにも定義されていないが_事実上_標準になっているイベントに加えられる追加の任意のフィールドや、それ自体のNIPを作るに値せず、他のNIPにも受け入れられない些細な仕様を追跡する。 10 | 11 | kind 0 12 | ====== 13 | 14 | これらはメタデータイベントの文字列化されたJSONに存在する可能性のあるNIP-01で指定されていない追加のフィールドである。 15 | 16 | - `display_name`: `name`より豊富な文字数を持ち、代替となるより大きな名前。`name`はメタデータ内の`display_name`の存在に関わらず常に設定するべきである。 17 | - `website`: イベントの著者になんらかの形で関係するウェブのURL。 18 | - `banner`: プロフィール画面の背景に任意で掲示される幅広 (~1024x768) の画像のURL。 19 | - `bot`: チャットボットやニュースフィードのように、コンテンツが全体的または部分的に自動化された結果であることを明確にするためのブール値。 20 | 21 | ### 廃止されたフィールド 22 | 23 | これらは実際に見つかった場合無視または削除されるべきフィールドである。 24 | 25 | - `displayName`: 代わりに`display_name`を使う。 26 | - `username`: 代わりに`name`を使う。 27 | 28 | kind 3 29 | ====== 30 | 31 | これらはフォローイベントの文字列化されたJSONに存在する可能性のあるNIP-02で指定されていない追加のフィールドである。 32 | 33 | ### 廃止されたフィールド 34 | 35 | - `{: {"read": , "write": }, ...}`: ユーザーが読み書きに使うリレー群のオブジェクト。代わりに[NIP-65](65.md)を使うべきである。 36 | 37 | タグ 38 | ==== 39 | 40 | これらのタグは複数のイベントkindに存在する。より具体的なNIPで異なる意味が指定されていない場合は、以下の意味を持つ。 41 | 42 | - `r`: イベントがなんらかの形で参照しているウェブのURL。 43 | - `i`: イベントが何らかの形で参照している外部ID - [NIP-73](73.md)を参照。 44 | - `title`: [NIP-51](51.md) セット、[NIP-52](52.md)カレンダーイベント、[NIP-53](53.md)ライブイベント、[NIP-99](99.md)リストの名前。 45 | - `t`: ハッシュタグ。小文字の文字列でなければならない (MUST)。 46 | -------------------------------------------------------------------------------- /25.md: -------------------------------------------------------------------------------- 1 | 2 | NIP-25 3 | ====== 4 | 5 | リアクション 6 | --------- 7 | 8 | `draft` `optional` 9 | 10 | リアクションとは`kind 7`のイベントであり、他のイベントに対しての反応を表現するために使用される。 11 | 12 | `content`が`+`の文字列のものが汎用的なリアクションとして定められており、 13 | これは「いいね」または「賛成」(それぞれ原文ではlike, upvote)として解釈されるべきである (SHOULD) 。 14 | 15 | `content`が`-`であるリアクションは「低評価」または「反対」(それぞれ原文ではdislike, downvote)として解釈されるべきである (SHOULD) 。 16 | また、これらは「いいね」としてカウントされるべきではなく (SHOULD NOT) 、 17 | 低評価・反対として投稿に表示してよい (MAY) 。 18 | クライアントはこれらを表示する際、redditのように高評価数-低評価数として1つの数値に集計して表示するか、 19 | あるいはそのまま分けて表示してよい (MAY) 。 20 | 21 | `content`には、絵文字及びカスタム絵文字([NIP-30](30.md))を利用してもよく (MAY) 、この場合「高評価」あるいは「低評価」として解釈してもよい (MAY) 。 22 | また、クライアントは絵文字によるリアクションを投稿に表示してもよい (MAY) 。 23 | もし`content`が空文字列だった場合は、クライアントはそれを"+"として解釈すべきである。 24 | 25 | タグ 26 | ---- 27 | 28 | リアクションイベントには、リアクション対象としているイベントに含まれる`e`及び`p`タグを含めるべきである (SHOULD) 。ターゲットが置換可能なイベントの場合、任意で`a`タグを含めることができる。これにより、各ユーザはメンションされている投稿へのリアクションがあった際、通知を受け取ることが可能になる。また、クライアントはこのようにして付与された`e`タグを使用することで、1つの投稿あるいはスレッド全体に関連するリアクションを取得できる。`a`タグを使用すると、クライアントは置換可能なイベントの全てのバージョンに対するリアクションを取得できる。 29 | 30 | タグのリストの中で最後に現れる`e`タグでは、リアクション対象となるノートの`id`を指定しなければならない (MUST) 。 31 | 32 | タグのリストの中で最後に現れる`p`タグでは、リアクション対象となるイベントの`pubkey`を指定しなければならない (MUST) 。 33 | 34 | `a`タグでは、リアクション対象となる置換可能なものの座標 (`kind:pubkey:d-tag`) を含めなければならない (MUST) 。 35 | 36 | リアクションイベントには、リアクションされたイベントの文字列化されたkind番号を値とする 37 | `k`タグを含めてもよい (MAY) 。 38 | 39 | Example code 40 | 41 | ```swift 42 | func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent { 43 | var tags: [[String]] = liked.tags.filter { 44 | tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") 45 | } 46 | tags.append(["e", liked.id]) 47 | tags.append(["p", liked.pubkey]) 48 | tags.append(["k", liked.kind]) 49 | let ev = NostrEvent(content: "+", pubkey: pubkey, kind: 7, tags: tags) 50 | ev.calculate_id() 51 | ev.sign(privkey: privkey) 52 | return ev 53 | } 54 | ``` 55 | 56 | ウェブサイトへのリアクション 57 | --------------------- 58 | 59 | リアクションのターゲットがWebサイトの場合、リアクションは`kind 17`イベントでなければならず (MUST) 、WebサイトのURLを含む`r`タグを含めなければならない (MUST) 。 60 | 61 | ```jsonc 62 | { 63 | "kind": 17, 64 | "content": "⭐", 65 | "tags": [ 66 | ["r", "https://example.com/"] 67 | ], 68 | // other fields... 69 | } 70 | ``` 71 | 72 | 同じWebサイトへのリアクションがクエリから省略されないように、URLは[正規化](https://datatracker.ietf.org/doc/html/rfc3986#section-6)される必要がある。 73 | ページのセクションにリアクションを付けるために、フラグメントをURLに追加してもよい (MAY)。 74 | フラグメントを含むURLは、元と同じURLとはみなされないことに注意すべきである。 75 | 76 | カスタム絵文字によるリアクション 77 | --------------------- 78 | 79 | クライアントは、リアクション内容として[NIP-30](30.md)のカスタム絵文字における`:shortcode:`を使用してもよい。 80 | ショートコードがリアクションに含まれる場合、クライアントはemojiタグを参照し、 81 | その内容を絵文字として描画すべきである。 82 | 83 | ```jsonc 84 | { 85 | "kind": 7, 86 | "content": ":soapbox:", 87 | "tags": [ 88 | ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"] 89 | ], 90 | // other fields... 91 | } 92 | ``` 93 | 94 | `content`に含めて良い`:shortcode:`は1つのみで、emojiタグについても同様に1つのみ指定可能である。 95 | -------------------------------------------------------------------------------- /26.md: -------------------------------------------------------------------------------- 1 | NIP-26 2 | ======= 3 | 4 | イベント署名の委任 5 | ----------------------- 6 | 7 | `draft` `optional` 8 | 9 | このNIPは他の鍵ペアを使ってイベントを署名するための委任の方法を定義する。 10 | 11 | この提案の活用法として、クライアントと対話するときにルート鍵ペアの使用を回避できることが挙げられる。例えば、ユーザーは使用したいクライアント毎に新しい鍵ペアを作成し、コールドストレージへ保存するルート鍵ペアと代えて新しい鍵ペアにイベントを署名させることができる。 12 | 13 | #### 'delegation'タグの紹介 14 | 15 | このNIPで導入される`delegation`タグは次のような書式で示される。 16 | 17 | ```json 18 | [ 19 | "delegation", 20 | , 21 | , 22 | 23 | ] 24 | ``` 25 | 26 | ##### 委任トークン 27 | 28 | **委任トークン**は次の文字列のsha256ハッシュへの64バイトのシュノア署名でなければならない。 (訳注: 以下に示される文字列は、委任トークン**ではない**。委任文字列である。) 29 | 30 | ``` 31 | nostr:delegation:: 32 | ``` 33 | 34 | ##### クエリ文字列の条件句 35 | 36 | 上記のクエリ文字列には、以下のフィールドと演算子が対応している。 37 | 38 | *フィールド*: 39 | 1. `kind` 40 | - *演算子*: 41 | - `=${KIND_NUMBER}` - 委任された鍵は、指定されたイベントkindのみに署名できる。 42 | 2. `created_at` 43 | - *演算子*: 44 | - `<${TIMESTAMP}` - 委任された鍵は、指定されたタイムスタンプより***過去***に作成されたイベントにのみ署名できる。 45 | - `>${TIMESTAMP}` - 委任された鍵は、指定されたタイムスタンプより***未来***に作成されたイベントにのみ署名できる。 46 | 47 | 単一の条件を指定するには、対応しているフィールドと演算子を使用する必要が有る。複数の条件を1つのクエリ文字列で指定する場合、条件句は`&`で組み合わされる必要が有る。 48 | 49 | 例えば、次に示される条件句の文字列は有効である。 50 | 51 | - `kind=1&created_at<1675721813` 52 | - `kind=0&kind=1&created_at>1675721813` 53 | - `kind=1&created_at>1674777689&created_at<1675721813` 54 | 55 | 大半のユースケースでは、以下の事が推奨される。 56 | 1. クエリ文字列には、現在の時刻を反映した`created_at`より***未来***のみの条件を含めるべきである。 57 | 2. クエリ文字列には、空でなく、極端に遠い未来の時刻でない`created_at`より***過去***のみの条件を含めるべきである。もし、委任が時間的範囲に拘束されない場合、ルート鍵ペアを認証に利用するのと同様のセキュリティリスクが発生する。 58 | 59 | #### 例 60 | 61 | ``` 62 | # 委任する鍵: 63 | privkey: ee35e8bb71131c02c1d7e73231daa48e9953d329a4b701f7133c8f46dd21139c 64 | pubkey: 8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd 65 | 66 | # 委任される鍵: 67 | privkey: 777e4f60b4aa87937e13acc84f7abcc3c93cc035cb4c1e9f7a9086dd78fffce1 68 | pubkey: 477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396 69 | ``` 70 | 71 | 次に示す委任文字列は、現在のタイムスタンプが`1674834236`のとき今から30日間、委任される鍵 (477318cf...) へノートの署名権限を付与するものである。 72 | ```json 73 | nostr:delegation:477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396:kind=1&created_at>1674834236&created_at<1677426236 74 | ``` 75 | 76 | 次に示す委任トークンは、上記の委任文字列のSHA256ハッシュへの委任する鍵 (8e0d3d3e...) による署名である。 77 | ``` 78 | 6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524 79 | ``` 80 | 81 | 委任された鍵 (477318cf...) は委任する鍵 (8e0d3d3e...) に代わってイベントを構築できる。委任された鍵は自身の秘密鍵でイベントに署名して公開する。 82 | ```json 83 | { 84 | "id": "e93c6095c3db1c31d15ac771f8fc5fb672f6e52cd25505099f62cd055523224f", 85 | "pubkey": "477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396", 86 | "created_at": 1677426298, 87 | "kind": 1, 88 | "tags": [ 89 | [ 90 | "delegation", 91 | "8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd", 92 | "kind=1&created_at>1674834236&created_at<1677426236", 93 | "6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524" 94 | ] 95 | ], 96 | "content": "Hello, world!", 97 | "sig": "633db60e2e7082c13a47a6b19d663d45b2a2ebdeaf0b4c35ef83be2738030c54fc7fd56d139652937cdca875ee61b51904a1d0d0588a6acd6168d7be2909d693" 98 | } 99 | ``` 100 | 101 | 委任トークンの署名検証の結果が有効で、条件を満たしている (この例では`kind=1`、`created_at>1674834236`、`created_at<1677426236`) 場合、委任は有効だと見做される。 102 | 103 | クライアントは、委任されたノートをあたかも委任した鍵 (8e0d3d3e...) によって直接発行されたかのように表示する必要が有る。 104 | 105 | 106 | #### リレーとクライアントの対応 107 | 108 | リレーは`["REQ", "", {"authors": ["A"]}]`のようなリクエストに対して`pubkey`とdelegationタグ`[1]` の値の両方を問い合わせることで答えるべきである。 109 | 110 | リレーは、委任される鍵 (477318cf...) によって公開されたイベントを委任する鍵 (8e0d3d3e...) が削除することを許可するべきである (SHOULD)。 111 | -------------------------------------------------------------------------------- /27.md: -------------------------------------------------------------------------------- 1 | NIP-27 2 | ====== 3 | 4 | Text Note References 5 | -------------------- 6 | 7 | `draft` `optional` 8 | 9 | This document standardizes the treatment given by clients of inline references of other events and profiles inside the `.content` of any event that has readable text in its `.content` (such as kinds 1 and 30023). 10 | 11 | When creating an event, clients should include mentions to other profiles and to other events in the middle of the `.content` using [NIP-21](21.md) codes, such as `nostr:nprofile1qqsw3dy8cpu...6x2argwghx6egsqstvg`. 12 | 13 | Including [NIP-10](10.md)-style tags (`["e", , , ]`) for each reference is optional, clients should do it whenever they want the profile being mentioned to be notified of the mention, or when they want the referenced event to recognize their mention as a reply. 14 | 15 | A reader client that receives an event with such `nostr:...` mentions in its `.content` can do any desired context augmentation (for example, linking to the profile or showing a preview of the mentioned event contents) it wants in the process. If turning such mentions into links, they could become internal links, [NIP-21](21.md) links or direct links to web clients that will handle these references. 16 | 17 | --- 18 | 19 | ## Example of a profile mention process 20 | 21 | Suppose Bob is writing a note in a client that has search-and-autocomplete functionality for users that is triggered when they write the character `@`. 22 | 23 | As Bob types `"hello @mat"` the client will prompt him to autocomplete with [mattn's profile](https://njump.me/npub1937vv2nf06360qn9y8el6d8sevnndy7tuh5nzre4gj05xc32tnwqauhaj6), showing a picture and name. 24 | 25 | Bob presses "enter" and now he sees his typed note as `"hello @mattn"`, `@mattn` is highlighted, indicating that it is a mention. Internally, however, the event looks like this: 26 | 27 | ```json 28 | { 29 | "content": "hello nostr:nprofile1qqszclxx9f5haga8sfjjrulaxncvkfekj097t6f3pu65f86rvg49ehqj6f9dh", 30 | "created_at": 1679790774, 31 | "id": "f39e9b451a73d62abc5016cffdd294b1a904e2f34536a208874fe5e22bbd47cf", 32 | "kind": 1, 33 | "pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 34 | "sig": "f8c8bab1b90cc3d2ae1ad999e6af8af449ad8bb4edf64807386493163e29162b5852a796a8f474d6b1001cddbaac0de4392838574f5366f03cc94cf5dfb43f4d", 35 | "tags": [ 36 | [ 37 | "p", 38 | "2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc" 39 | ] 40 | ] 41 | } 42 | ``` 43 | 44 | (Alternatively, the mention could have been a `nostr:npub1...` URL.) 45 | 46 | After Bob publishes this event and Carol sees it, her client will initially display the `.content` as it is, but later it will parse the `.content` and see that there is a `nostr:` URL in there, decode it, extract the public key from it (and possibly relay hints), fetch that profile from its internal database or relays, then replace the full URL with the name `@mattn`, with a link to the internal page view for that profile. 47 | 48 | ## Verbose and probably unnecessary considerations 49 | 50 | - The example above was very concrete, but it doesn't mean all clients have to implement the same flow. There could be clients that do not support autocomplete at all, so they just allow users to paste raw [NIP-19](19.md) codes into the body of text, then prefix these with `nostr:` before publishing the event. 51 | - The flow for referencing other events is similar: a user could paste a `note1...` or `nevent1...` code and the client will turn that into a `nostr:note1...` or `nostr:nevent1...` URL. Then upon reading such references the client may show the referenced note in a preview box or something like that -- or nothing at all. 52 | - Other display procedures can be employed: for example, if a client that is designed for dealing with only `kind:1` text notes sees, for example, a [`kind:30023`](23.md) `nostr:naddr1...` URL reference in the `.content`, it can, for example, decide to turn that into a link to some hardcoded webapp capable of displaying such events. 53 | - Clients may give the user the option to include or not include tags for mentioned events or profiles. If someone wants to mention `mattn` without notifying them, but still have a nice augmentable/clickable link to their profile inside their note, they can instruct their client to _not_ create a `["p", ...]` tag for that specific mention. 54 | - In the same way, if someone wants to reference another note but their reference is not meant to show up along other replies to that same note, their client can choose to not include a corresponding `["e", ...]` tag for any given `nostr:nevent1...` URL inside `.content`. Clients may decide to expose these advanced functionalities to users or be more opinionated about things. 55 | -------------------------------------------------------------------------------- /28.md: -------------------------------------------------------------------------------- 1 | 2 | NIP-28 3 | ====== 4 | 5 | Public Chat 6 | ----------- 7 | 8 | `draft` `optional` 9 | 10 | This NIP defines new event kinds for public chat channels, channel messages, and basic client-side moderation. 11 | 12 | It reserves five event kinds (40-44) for immediate use: 13 | 14 | - `40 - channel create` 15 | - `41 - channel metadata` 16 | - `42 - channel message` 17 | - `43 - hide message` 18 | - `44 - mute user` 19 | 20 | Client-centric moderation gives client developers discretion over what types of content they want included in their apps, while imposing no additional requirements on relays. 21 | 22 | ## Kind 40: Create channel 23 | 24 | Create a public chat channel. 25 | 26 | In the channel creation `content` field, Client SHOULD include basic channel metadata (`name`, `about`, `picture` and `relays` as specified in kind 41). 27 | 28 | ```jsonc 29 | { 30 | "content": "{\"name\": \"Demo Channel\", \"about\": \"A test channel.\", \"picture\": \"https://placekitten.com/200/200\", \"relays\": [\"wss://nos.lol\", \"wss://nostr.mom\"]}", 31 | // other fields... 32 | } 33 | ``` 34 | 35 | 36 | ## Kind 41: Set channel metadata 37 | 38 | Update a channel's public metadata. 39 | 40 | Kind 41 is used to update the metadata without modifying the event id for the channel. Only the most recent kind 41 per `e` tag value MAY be available. 41 | 42 | Clients SHOULD ignore kind 41s from pubkeys other than the kind 40 pubkey. 43 | 44 | Clients SHOULD support basic metadata fields: 45 | 46 | - `name` - string - Channel name 47 | - `about` - string - Channel description 48 | - `picture` - string - URL of channel picture 49 | - `relays` - array - List of relays to download and broadcast events to 50 | 51 | Clients MAY add additional metadata fields. 52 | 53 | Clients SHOULD use [NIP-10](10.md) marked "e" tags to recommend a relay. 54 | 55 | ```jsonc 56 | { 57 | "content": "{\"name\": \"Updated Demo Channel\", \"about\": \"Updating a test channel.\", \"picture\": \"https://placekitten.com/201/201\", \"relays\": [\"wss://nos.lol\", \"wss://nostr.mom\"]}", 58 | "tags": [["e", , ]], 59 | // other fields... 60 | } 61 | ``` 62 | 63 | 64 | ## Kind 42: Create channel message 65 | 66 | Send a text message to a channel. 67 | 68 | Clients SHOULD use [NIP-10](10.md) marked "e" tags to recommend a relay and specify whether it is a reply or root message. 69 | 70 | Clients SHOULD append [NIP-10](10.md) "p" tags to replies. 71 | 72 | Root message: 73 | 74 | ```jsonc 75 | { 76 | "content": , 77 | "tags": [["e", , , "root"]], 78 | // other fields... 79 | } 80 | ``` 81 | 82 | Reply to another message: 83 | 84 | ```jsonc 85 | { 86 | "content": , 87 | "tags": [ 88 | ["e", , , "root"], 89 | ["e", , , "reply"], 90 | ["p", , ], 91 | // rest of tags... 92 | ], 93 | // other fields... 94 | } 95 | ``` 96 | 97 | 98 | ## Kind 43: Hide message 99 | 100 | User no longer wants to see a certain message. 101 | 102 | The `content` may optionally include metadata such as a `reason`. 103 | 104 | Clients SHOULD hide event 42s shown to a given user, if there is an event 43 from that user matching the event 42 `id`. 105 | 106 | Clients MAY hide event 42s for other users other than the user who sent the event 43. 107 | 108 | (For example, if three users 'hide' an event giving a reason that includes the word 'pornography', a Nostr client that is an iOS app may choose to hide that message for all iOS clients.) 109 | 110 | ```jsonc 111 | { 112 | "content": "{\"reason\": \"Dick pic\"}", 113 | "tags": [["e", ]], 114 | // other fields... 115 | } 116 | ``` 117 | 118 | ## Kind 44: Mute user 119 | 120 | User no longer wants to see messages from another user. 121 | 122 | The `content` may optionally include metadata such as a `reason`. 123 | 124 | Clients SHOULD hide event 42s shown to a given user, if there is an event 44 from that user matching the event 42 `pubkey`. 125 | 126 | Clients MAY hide event 42s for users other than the user who sent the event 44. 127 | 128 | ```jsonc 129 | { 130 | "content": "{\"reason\": \"Posting dick pics\"}", 131 | "tags": [["p", ]], 132 | // other fields... 133 | } 134 | ``` 135 | 136 | ## Relay recommendations 137 | 138 | Clients SHOULD use the relay URLs of the metadata events. 139 | 140 | Clients MAY use any relay URL. For example, if a relay hosting the original kind 40 event for a channel goes offline, clients could instead fetch channel data from a backup relay, or a relay that clients trust more than the original relay. 141 | 142 | Motivation 143 | ---------- 144 | If we're solving censorship-resistant communication for social media, we may as well solve it also for Telegram-style messaging. 145 | 146 | We can bring the global conversation out from walled gardens into a true public square open to all. 147 | 148 | 149 | Additional info 150 | --------------- 151 | 152 | - [Chat demo PR with fiatjaf+jb55 comments](https://github.com/ArcadeCity/arcade/pull/28) 153 | - [Conversation about NIP16](https://t.me/nostr_protocol/29566) 154 | -------------------------------------------------------------------------------- /30.md: -------------------------------------------------------------------------------- 1 | NIP-30 2 | ====== 3 | 4 | カスタム絵文字 5 | ------------ 6 | 7 | `draft` `optional` 8 | 9 | 以下の形式の`"emoji`タグを1つ以上含めることで、**kind 0**、**kind 1**、**kind 7**及び**kind 30315**のイベントに対し、カスタム絵文字を付与してもよい: 10 | 11 | ``` 12 | ["emoji", , ] 13 | ``` 14 | 15 | ここで、``及び``は以下の通りである: 16 | 17 | - ``は絵文字の名前であり、英数字及びアンダースコアのみによって構成されなければならない (MUST) 。 18 | - ``は対応する絵文字の画像ファイルのURLである。 19 | 20 | それぞれの`"emoji"`タグについて、クライアントは`:shortcode:`といった形式で表される絵文字ショートコードを解釈し、カスタム絵文字を表示する(なおこの操作を「絵文字化」という)べきである。 21 | 22 | クライアントはユーザに対し、`:shortcode:`識別子を使用することでイベントにカスタム絵文字を含める機能、対応する`"emoji"`タグを付与する機能を提供してもよい。 23 | 24 | ### Kind 0 イベント 25 | 26 | kind 0のイベントにおいては、`name`及び`about`フィールドにカスタム絵文字を使用してよく、クライアントはこれを「絵文字化」すべきである。 27 | 28 | ```json 29 | { 30 | "kind": 0, 31 | "content": "{\"name\":\"Alex Gleason :soapbox:\"}", 32 | "tags": [ 33 | ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"] 34 | ], 35 | "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6", 36 | "created_at": 1682790000 37 | } 38 | ``` 39 | 40 | ### Kind 1 イベント 41 | 42 | kind 1のイベントにおいては、`content`フィールドにカスタム絵文字を使用してよく、クライアントはこれを「絵文字化」すべきである。 43 | 44 | ```json 45 | { 46 | "kind": 1, 47 | "content": "Hello :gleasonator: 😂 :ablobcatrainbow: :disputed: yolo", 48 | "tags": [ 49 | ["emoji", "ablobcatrainbow", "https://gleasonator.com/emoji/blobcat/ablobcatrainbow.png"], 50 | ["emoji", "disputed", "https://gleasonator.com/emoji/Fun/disputed.png"], 51 | ["emoji", "gleasonator", "https://gleasonator.com/emoji/Gleasonator/gleasonator.png"] 52 | ], 53 | "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6", 54 | "created_at": 1682630000 55 | } 56 | ``` 57 | 58 | ### Kind 7 events 59 | 60 | In kind 7 events, the `content` should be emojified. 61 | 62 | ```json 63 | { 64 | "kind": 7, 65 | "content": ":dezh:", 66 | "tags": [ 67 | ["emoji", "dezh", "https://raw.githubusercontent.com/dezh-tech/brand-assets/main/dezh/logo/black-normal.svg"] 68 | ], 69 | "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6", 70 | "created_at": 1682630000 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /31.md: -------------------------------------------------------------------------------- 1 | NIP-31 2 | ====== 3 | 4 | Dealing with unknown event kinds 5 | -------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | When creating a new custom event kind that is part of a custom protocol and isn't meant to be read as text (like `kind:1`), clients should use an `alt` tag to write a short human-readable plaintext summary of what that event is about. 10 | 11 | The intent is that social clients, used to display only `kind:1` notes, can still show something in case a custom event pops up in their timelines. The content of the `alt` tag should provide enough context for a user that doesn't know anything about this event kind to understand what it is. 12 | 13 | These clients that only know `kind:1` are not expected to ask relays for events of different kinds, but users could still reference these weird events on their notes, and without proper context these could be nonsensical notes. Having the fallback text makes that situation much better -- even if only for making the user aware that they should try to view that custom event elsewhere. 14 | 15 | `kind:1`-centric clients can make interacting with these event kinds more functional by supporting [NIP-89](https://github.com/nostr-protocol/nips/blob/master/89.md). 16 | -------------------------------------------------------------------------------- /32.md: -------------------------------------------------------------------------------- 1 | NIP-32 2 | ====== 3 | 4 | Labeling 5 | -------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines two new indexable tags to label events and a new event kind (`kind:1985`) to attach those labels to existing events. This supports several use cases, including distributed moderation, collection management, license assignment, and content classification. 10 | 11 | New Tags: 12 | 13 | - `L` denotes a label namespace 14 | - `l` denotes a label 15 | 16 | Label Namespace Tag 17 | ---- 18 | 19 | An `L` tag can be any string, but publishers SHOULD ensure they are unambiguous by using a well-defined namespace 20 | (such as an ISO standard) or reverse domain name notation. 21 | 22 | `L` tags are RECOMMENDED in order to support searching by namespace rather than by a specific tag. The special `ugc` 23 | ("user generated content") namespace MAY be used when the label content is provided by an end user. 24 | 25 | `L` tags starting with `#` indicate that the label target should be associated with the label's value. 26 | This is a way of attaching standard nostr tags to events, pubkeys, relays, urls, etc. 27 | 28 | Label Tag 29 | ---- 30 | 31 | An `l` tag's value can be any string. If using an `L` tag, `l` tags MUST include a mark matching an `L` 32 | tag value in the same event. If no `L` tag is included, a mark SHOULD still be included. If none is 33 | included, `ugc` is implied. 34 | 35 | Label Target 36 | ---- 37 | 38 | The label event MUST include one or more tags representing the object or objects being 39 | labeled: `e`, `p`, `a`, `r`, or `t` tags. This allows for labeling of events, people, relays, 40 | or topics respectively. As with NIP-01, a relay hint SHOULD be included when using `e` and 41 | `p` tags. 42 | 43 | Content 44 | ------- 45 | 46 | Labels should be short, meaningful strings. Longer discussions, such as for an 47 | explanation of why something was labeled the way it was, should go in the event's `content` field. 48 | 49 | Self-Reporting 50 | ------- 51 | 52 | `l` and `L` tags MAY be added to other event kinds to support self-reporting. For events 53 | with a kind other than 1985, labels refer to the event itself. 54 | 55 | Example events 56 | -------------- 57 | 58 | A suggestion that multiple pubkeys be associated with the `permies` topic. 59 | 60 | ```jsonc 61 | { 62 | "kind": 1985, 63 | "tags": [ 64 | ["L", "#t"], 65 | ["l", "permies", "#t"], 66 | ["p", , ], 67 | ["p", , ] 68 | ], 69 | // other fields... 70 | } 71 | ``` 72 | 73 | A report flagging violence toward a human being as defined by ontology.example.com. 74 | 75 | ```jsonc 76 | { 77 | "kind": 1985, 78 | "tags": [ 79 | ["L", "com.example.ontology"], 80 | ["l", "VI-hum", "com.example.ontology"], 81 | ["p", , ], 82 | ["p", , ] 83 | ], 84 | // other fields... 85 | } 86 | ``` 87 | 88 | A moderation suggestion for a chat event. 89 | 90 | ```jsonc 91 | { 92 | "kind": 1985, 93 | "tags": [ 94 | ["L", "nip28.moderation"], 95 | ["l", "approve", "nip28.moderation"], 96 | ["e", , ] 97 | ], 98 | // other fields... 99 | } 100 | ``` 101 | 102 | Assignment of a license to an event. 103 | 104 | ```jsonc 105 | { 106 | "kind": 1985, 107 | "tags": [ 108 | ["L", "license"], 109 | ["l", "MIT", "license"], 110 | ["e", , ] 111 | ], 112 | // other fields... 113 | } 114 | ``` 115 | 116 | Publishers can self-label by adding `l` tags to their own non-1985 events. In this case, the kind 1 event's author 117 | is labeling their note as being related to Milan, Italy using ISO 3166-2. 118 | 119 | ```jsonc 120 | { 121 | "kind": 1, 122 | "tags": [ 123 | ["L", "ISO-3166-2"], 124 | ["l", "IT-MI", "ISO-3166-2"] 125 | ], 126 | "content": "It's beautiful here in Milan!", 127 | // other fields... 128 | } 129 | ``` 130 | 131 | Author is labeling their note language as English using ISO-639-1. 132 | 133 | ```jsonc 134 | { 135 | "kind": 1, 136 | "tags": [ 137 | ["L", "ISO-639-1"], 138 | ["l", "en", "ISO-639-1"] 139 | ], 140 | "content": "English text", 141 | // other fields... 142 | } 143 | ``` 144 | 145 | Other Notes 146 | ----------- 147 | 148 | When using this NIP to bulk-label many targets at once, events may be requested for deletion using [NIP-09](09.md) and a replacement 149 | may be published. We have opted not to use addressable/replaceable events for this due to the 150 | complexity in coming up with a standard `d` tag. In order to avoid ambiguity when querying, 151 | publishers SHOULD limit labeling events to a single namespace. 152 | 153 | Before creating a vocabulary, explore how your use case may have already been designed and 154 | imitate that design if possible. Reverse domain name notation is encouraged to avoid 155 | namespace clashes, but for the sake of interoperability all namespaces should be 156 | considered open for public use, and not proprietary. In other words, if there is a 157 | namespace that fits your use case, use it even if it points to someone else's domain name. 158 | 159 | Vocabularies MAY choose to fully qualify all labels within a namespace (for example, 160 | `["l", "com.example.vocabulary:my-label"]`. This may be preferred when defining more 161 | formal vocabularies that should not be confused with another namespace when querying 162 | without an `L` tag. For these vocabularies, all labels SHOULD include the namespace 163 | (rather than mixing qualified and unqualified labels). 164 | 165 | A good heuristic for whether a use case fits this NIP is whether labels would ever be unique. 166 | For example, many events might be labeled with a particular place, topic, or pubkey, but labels 167 | with specific values like "John Doe" or "3.18743" are not labels, they are values, and should 168 | be handled in some other way. 169 | 170 | 171 | Appendix: Known Ontologies 172 | -------------------------- 173 | 174 | Below is a non-exhaustive list of ontologies currently in widespread use. 175 | 176 | - [social.ontolo.categories](https://ontolo.social/) 177 | -------------------------------------------------------------------------------- /33.md: -------------------------------------------------------------------------------- 1 | NIP-33 2 | ====== 3 | 4 | Parameterized Replaceable Events 5 | -------------------------------- 6 | 7 | `final` `mandatory` 8 | 9 | Renamed to "Addressable events" and moved to [NIP-01](01.md). 10 | -------------------------------------------------------------------------------- /34.md: -------------------------------------------------------------------------------- 1 | NIP-34 2 | ====== 3 | 4 | `git` stuff 5 | ----------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines all the ways code collaboration using and adjacent to [`git`](https://git-scm.com/) can be done using Nostr. 10 | 11 | ## Repository announcements 12 | 13 | Git repositories are hosted in Git-enabled servers, but their existence can be announced using Nostr events, as well as their willingness to receive patches, bug reports and comments in general. 14 | 15 | ```jsonc 16 | { 17 | "kind": 30617, 18 | "content": "", 19 | "tags": [ 20 | ["d", ""], // usually kebab-case short name 21 | ["name", ""], 22 | ["description", "brief human-readable project description>"], 23 | ["web", "", ...], // a webpage url, if the git server being used provides such a thing 24 | ["clone", "", ...], // a url to be given to `git clone` so anyone can clone it 25 | ["relays", "", ...] // relays that this repository will monitor for patches and issues 26 | ["r", "", "euc"] 27 | ["maintainers", "", ...] 28 | ] 29 | } 30 | ``` 31 | 32 | The tags `web`, `clone`, `relays`, `maintainers` can have multiple values. 33 | 34 | The `r` tag annotated with the `"euc"` marker should be the commit ID of the earliest unique commit of this repo, made to identify it among forks and group it with other repositories hosted elsewhere that may represent essentially the same project. In most cases it will be the root commit of a repository. In case of a permanent fork between two projects, then the first commit after the fork should be used. 35 | 36 | Except `d`, all tags are optional. 37 | 38 | ## Repository state announcements 39 | 40 | An optional source of truth for the state of branches and tags in a repository. 41 | 42 | ```jsonc 43 | { 44 | "kind": 30618, 45 | "content": "", 46 | "tags": [ 47 | ["d", ""], // matches the identifier in the coresponding repository announcement 48 | ["refs//",""] 49 | ["HEAD", "ref: refs/heads/"] 50 | ] 51 | } 52 | ``` 53 | 54 | The `refs` tag may appear multiple times, or none. 55 | 56 | If no `refs` tags are present, the author is no longer tracking repository state using this event. This approach enables the author to restart tracking state at a later time unlike [NIP-09](09.md) deletion requests. 57 | 58 | The `refs` tag can be optionally extended to enable clients to identify how many commits ahead a ref is: 59 | 60 | ```jsonc 61 | { 62 | "tags": [ 63 | ["refs//", "", "", "", ...], 64 | ] 65 | } 66 | ``` 67 | 68 | ## Patches 69 | 70 | Patches can be sent by anyone to any repository. Patches to a specific repository SHOULD be sent to the relays specified in that repository's announcement event's `"relays"` tag. Patch events SHOULD include an `a` tag pointing to that repository's announcement address. 71 | 72 | Patches in a patch set SHOULD include a NIP-10 `e` `reply` tag pointing to the previous patch. 73 | 74 | The first patch revision in a patch revision SHOULD include a NIP-10 `e` `reply` to the original root patch. 75 | 76 | ```jsonc 77 | { 78 | "kind": 1617, 79 | "content": "", // contents of 80 | "tags": [ 81 | ["a", "30617::"], 82 | ["r", ""] // so clients can subscribe to all patches sent to a local git repo 83 | ["p", ""], 84 | ["p", ""], // optionally send the patch to another user to bring it to their attention 85 | 86 | ["t", "root"], // omitted for additional patches in a series 87 | // for the first patch in a revision 88 | ["t", "root-revision"], 89 | 90 | // optional tags for when it is desirable that the merged patch has a stable commit id 91 | // these fields are necessary for ensuring that the commit resulting from applying a patch 92 | // has the same id as it had in the proposer's machine -- all these tags can be omitted 93 | // if the maintainer doesn't care about these things 94 | ["commit", ""], 95 | ["r", ""] // so clients can find existing patches for a specific commit 96 | ["parent-commit", ""], 97 | ["commit-pgp-sig", "-----BEGIN PGP SIGNATURE-----..."], // empty string for unsigned commit 98 | ["committer", "", "", "", ""], 99 | ] 100 | } 101 | ``` 102 | 103 | The first patch in a series MAY be a cover letter in the format produced by `git format-patch`. 104 | 105 | ## Issues 106 | 107 | Issues are Markdown text that is just human-readable conversational threads related to the repository: bug reports, feature requests, questions or comments of any kind. Like patches, these SHOULD be sent to the relays specified in that repository's announcement event's `"relays"` tag. 108 | 109 | Issues may have a `subject` tag, which clients can utilize to display a header. Additionally, one or more `t` tags may be included to provide labels for the issue. 110 | 111 | ```json 112 | { 113 | "kind": 1621, 114 | "content": "", 115 | "tags": [ 116 | ["a", "30617::"], 117 | ["p", ""] 118 | ["subject", ""] 119 | ["t", ""] 120 | ["t", ""] 121 | ] 122 | } 123 | ``` 124 | 125 | ## Replies 126 | 127 | Replies are also Markdown text. The difference is that they MUST be issued as replies to either a `kind:1621` _issue_ or a `kind:1617` _patch_ event. The threading of replies and patches should follow NIP-10 rules. 128 | 129 | ```jsonc 130 | { 131 | "kind": 1622, 132 | "content": "", 133 | "tags": [ 134 | ["a", "30617::", ""], 135 | ["e", "", "", "root"], 136 | 137 | // other "e" and "p" tags should be applied here when necessary, following the threading rules of NIP-10 138 | ["p", "", "", "mention"], 139 | ["e", "", "", "reply"], 140 | // rest of tags... 141 | ], 142 | // other fields... 143 | } 144 | ``` 145 | 146 | ## Status 147 | 148 | Root Patches and Issues have a Status that defaults to 'Open' and can be set by issuing Status events. 149 | 150 | ```jsonc 151 | { 152 | "kind": 1630, // Open 153 | "kind": 1631, // Applied / Merged for Patches; Resolved for Issues 154 | "kind": 1632, // Closed 155 | "kind": 1633, // Draft 156 | "content": "", 157 | "tags": [ 158 | ["e", "", "", "root"], 159 | ["e", "", "", "reply"], // for when revisions applied 160 | ["p", ""], 161 | ["p", ""], 162 | ["p", ""], 163 | 164 | // optional for improved subscription filter efficiency 165 | ["a", "30617::", ""], 166 | ["r", ""] 167 | 168 | // optional for `1631` status 169 | ["e", "", "", "mention"], // for each 170 | // when merged 171 | ["merge-commit", ""] 172 | ["r", ""] 173 | // when applied 174 | ["applied-as-commits", "", ...] 175 | ["r", ""] // for each 176 | ] 177 | } 178 | ``` 179 | 180 | The Status event with the largest created_at date is valid. 181 | 182 | The Status of a patch-revision defaults to either that of the root-patch, or `1632` (Closed) if the root-patch's Status is `1631` and the patch-revision isn't tagged in the `1631` event. 183 | 184 | 185 | ## Possible things to be added later 186 | 187 | - "branch merge" kind (specifying a URL from where to fetch the branch to be merged) 188 | - inline file comments kind (we probably need one for patches and a different one for merged files) 189 | -------------------------------------------------------------------------------- /35.md: -------------------------------------------------------------------------------- 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 | ```json 41 | { 42 | "kind": 2003, 43 | "content": "", 44 | "tags": [ 45 | ["title", ""], 46 | ["x", ""], 47 | ["file", "", ""], 48 | ["file", "", ""], 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) -------------------------------------------------------------------------------- /36.md: -------------------------------------------------------------------------------- 1 | NIP-36 2 | ====== 3 | 4 | センシティブコンテンツ / コンテンツの警告 5 | ----------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | `content-warning`タグは、イベントのコンテンツを表示するために読者の承認が必要かどうかをユーザが指定できるようにする。 10 | クライアントは、ユーザが操作するまでコンテンツを非表示にできる。 11 | 12 | `l`および`L`タグは[NIP-32](32.md)で定義されているように`content-warning`や他の名前空間とともに使用してもよく、 13 | 更なる修飾とクエリをサポートする (MAY) 。 14 | 15 | #### 仕様 16 | 17 | ``` 18 | tag: content-warning 19 | options: 20 | - [reason]: optional 21 | ``` 22 | 23 | #### 例 24 | 25 | ```json 26 | { 27 | "pubkey": "", 28 | "created_at": 1000000000, 29 | "kind": 1, 30 | "tags": [ 31 | ["t", "hastag"], 32 | ["L", "content-warning"], 33 | ["l", "reason", "content-warning"], 34 | ["L", "social.nos.ontology"], 35 | ["l", "NS-nud", "social.nos.ontology"], 36 | ["content-warning", ""] 37 | ], 38 | "content": "sensitive content with #hastag\n", 39 | "id": "" 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /38.md: -------------------------------------------------------------------------------- 1 | 2 | NIP-38 3 | ====== 4 | 5 | User Statuses 6 | ------------- 7 | 8 | `draft` `optional` 9 | 10 | ## Abstract 11 | 12 | This NIP enables a way for users to share live statuses such as what music they are listening to, as well as what they are currently doing: work, play, out of office, etc. 13 | 14 | ## Live Statuses 15 | 16 | A special event with `kind:30315` "User Status" is defined as an *optionally expiring* _addressable event_, where the `d` tag represents the status type: 17 | 18 | For example: 19 | 20 | ```json 21 | { 22 | "kind": 30315, 23 | "content": "Sign up for nostrasia!", 24 | "tags": [ 25 | ["d", "general"], 26 | ["r", "https://nostr.world"] 27 | ], 28 | } 29 | ``` 30 | 31 | ```json 32 | { 33 | "kind": 30315, 34 | "content": "Intergalatic - Beastie Boys", 35 | "tags": [ 36 | ["d", "music"], 37 | ["r", "spotify:search:Intergalatic%20-%20Beastie%20Boys"], 38 | ["expiration", "1692845589"] 39 | ], 40 | } 41 | ``` 42 | 43 | Two common status types are defined: `general` and `music`. `general` represent general statuses: "Working", "Hiking", etc. 44 | 45 | `music` status events are for live streaming what you are currently listening to. The expiry of the `music` status should be when the track will stop playing. 46 | 47 | Any other status types can be used but they are not defined by this NIP. 48 | 49 | The status MAY include an `r`, `p`, `e` or `a` tag linking to a URL, profile, note, or addressable event. 50 | 51 | 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. 52 | 53 | # Client behavior 54 | 55 | Clients MAY display this next to the username on posts or profiles to provide live user status information. 56 | 57 | # Use Cases 58 | 59 | * Calendar nostr apps that update your general status when you're in a meeting 60 | * Nostr Nests that update your general status with a link to the nest when you join 61 | * Nostr music streaming services that update your music status when you're listening 62 | * Podcasting apps that update your music status when you're listening to a podcast, with a link for others to listen as well 63 | * Clients can use the system media player to update playing music status 64 | -------------------------------------------------------------------------------- /39.md: -------------------------------------------------------------------------------- 1 | NIP-39 2 | ====== 3 | 4 | External Identities in Profiles 5 | ------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | ## Abstract 10 | 11 | Nostr protocol users may have other online identities such as usernames, profile pages, keypairs etc. they control and they may want to include this data in their profile metadata so clients can parse, validate and display this information. 12 | 13 | ## `i` tag on a metadata event 14 | 15 | A new optional `i` tag is introduced for `kind 0` metadata event defined in [NIP-01](01.md): 16 | 17 | ```jsonc 18 | { 19 | "id": , 20 | "pubkey": , 21 | "tags": [ 22 | ["i", "github:semisol", "9721ce4ee4fceb91c9711ca2a6c9a5ab"], 23 | ["i", "twitter:semisol_public", "1619358434134196225"], 24 | ["i", "mastodon:bitcoinhackers.org/@semisol", "109775066355589974"] 25 | ["i", "telegram:1087295469", "nostrdirectory/770"] 26 | ], 27 | // other fields... 28 | } 29 | ``` 30 | 31 | An `i` tag will have two parameters, which are defined as the following: 32 | 1. `platform:identity`: This is the platform name (for example `github`) and the identity on that platform (for example `semisol`) joined together with `:`. 33 | 2. `proof`: String or object that points to the proof of owning this identity. 34 | 35 | Clients SHOULD process any `i` tags with more than 2 values for future extensibility. 36 | Identity provider names SHOULD only include `a-z`, `0-9` and the characters `._-/` and MUST NOT include `:`. 37 | Identity names SHOULD be normalized if possible by replacing uppercase letters with lowercase letters, and if there are multiple aliases for an entity the primary one should be used. 38 | 39 | ## Claim types 40 | 41 | ### `github` 42 | 43 | Identity: A GitHub username. 44 | 45 | Proof: A GitHub Gist ID. This Gist should be created by `` with a single file that has the text `Verifying that I control the following Nostr public key: `. 46 | This can be located at `https://gist.github.com//`. 47 | 48 | ### `twitter` 49 | 50 | Identity: A Twitter username. 51 | 52 | Proof: A Tweet ID. The tweet should be posted by `` and have the text `Verifying my account on nostr My Public Key: ""`. 53 | This can be located at `https://twitter.com//status/`. 54 | 55 | ### `mastodon` 56 | 57 | Identity: A Mastodon instance and username in the format `/@`. 58 | 59 | Proof: A Mastodon post ID. This post should be published by `@` and have the text `Verifying that I control the following Nostr public key: ""`. 60 | This can be located at `https:///`. 61 | 62 | ### `telegram` 63 | 64 | Identity: A Telegram user ID. 65 | 66 | Proof: A string in the format `/` which points to a message published in the public channel or group with name `` and message ID ``. This message should be sent by user ID `` and have the text `Verifying that I control the following Nostr public key: ""`. 67 | This can be located at `https://t.me/`. 68 | -------------------------------------------------------------------------------- /40.md: -------------------------------------------------------------------------------- 1 | NIP-40 2 | ====== 3 | 4 | Expiration Timestamp 5 | -------------------- 6 | 7 | `draft` `optional` 8 | 9 | The `expiration` tag enables users to specify a unix timestamp at which the message SHOULD be considered expired (by relays and clients) and SHOULD be deleted by relays. 10 | 11 | #### Spec 12 | 13 | ``` 14 | tag: expiration 15 | values: 16 | - [UNIX timestamp in seconds]: required 17 | ``` 18 | 19 | #### Example 20 | 21 | ```json 22 | { 23 | "pubkey": "", 24 | "created_at": 1000000000, 25 | "kind": 1, 26 | "tags": [ 27 | ["expiration", "1600000000"] 28 | ], 29 | "content": "This message will expire at the specified timestamp and be deleted by relays.\n", 30 | "id": "" 31 | } 32 | ``` 33 | 34 | Note: The timestamp should be in the same format as the created_at timestamp and should be interpreted as the time at which the message should be deleted by relays. 35 | 36 | Client Behavior 37 | --------------- 38 | 39 | Clients SHOULD use the `supported_nips` field to learn if a relay supports this NIP. Clients SHOULD NOT send expiration events to relays that do not support this NIP. 40 | 41 | Clients SHOULD ignore events that have expired. 42 | 43 | Relay Behavior 44 | -------------- 45 | 46 | Relays MAY NOT delete expired messages immediately on expiration and MAY persist them indefinitely. 47 | Relays SHOULD NOT send expired events to clients, even if they are stored. 48 | Relays SHOULD drop any events that are published to them if they are expired. 49 | An expiration timestamp does not affect storage of ephemeral events. 50 | 51 | Suggested Use Cases 52 | ------------------- 53 | 54 | * Temporary announcements - This tag can be used to make temporary announcements. For example, an event organizer could use this tag to post announcements about an upcoming event. 55 | * Limited-time offers - This tag can be used by businesses to make limited-time offers that expire after a certain amount of time. For example, a business could use this tag to make a special offer that is only available for a limited time. 56 | 57 | #### Warning 58 | The events could be downloaded by third parties as they are publicly accessible all the time on the relays. 59 | So don't consider expiring messages as a security feature for your conversations or other uses. 60 | -------------------------------------------------------------------------------- /42.md: -------------------------------------------------------------------------------- 1 | NIP-42 2 | ====== 3 | 4 | リレーに対するクライアントの認証 5 | ----------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | このNIPはクライアントが一時的なイベントに署名することでリレーに対して認証する方法を定義する。 10 | 11 | ## 動機 12 | 13 | リレーは、制限されたリソースにアクセスしようとするクライアントに認証を要求したくなる場合がある。例えば: 14 | 15 | - リレーはクライアントからのイベントの送信に対して支払いまたはその他の形式のホワイトリスト登録を要求する可能性がある。 -- これは素朴には送信できるイベントをホワイトリストに登録された鍵で署名されたもののみに制限することで達成されるが、このNIPを使用すれば、認証されたユーザから送信されたイベントであればどのようなイベントでもリレーはそれを受け入れるかを選択できる。 16 | - リレーは`kind: 4`DMへのアクセスをそれを交換している当事者のみに制限する可能性があり、そしてそのためにクラアントが同kindをクエリする前に認証を要求したいかもしれない。 17 | - リレーはあらゆる種類の購読を課金ユーザまたは他の何らかの手段でホワイトリスト登録されたユーザのみに限定する可能性があり、そのために認証が必要かもしれない。 18 | 19 | ## 定義 20 | 21 | ### クライアント-リレー間の新しいプロトコルメッセージ 22 | 23 | このNIPは新しいメッセージ`AUTH`を定義する。このメッセージを、リレーは認証をサポートするときクライアントに、クライアントは認証したいときリレーに送信してもよい (CAN)。リレーによって送信された場合、メッセージは以下の形式を取る: 24 | 25 | ``` 26 | ["AUTH", ] 27 | ``` 28 | 29 | クライアントによって送信された場合は、以下の形式を取る: 30 | 31 | ``` 32 | ["AUTH", ] 33 | ``` 34 | 35 | クライアントによって送信された`AUTH`メッセージに対しては、`EVENT`メッセージに対してそうするように、`OK`メッセージで応答されなければならない (MUST)。 36 | 37 | ### 正規の認証イベント 38 | 39 | 署名されたイベント (``) は送信またはクエリされることを意図していない一時的なイベントであり、`kind: 22242`でなければならず、かつ少なくとも2つのタグ -- すなわちひとつにリレーのURLを、そしてもうひとつにリレーから受け取ったチャレンジ文字列 (``) を持っているべきである。リレーは`kind: 22242`イベントをクライアントへのブロードキャストから除かなければならない (MUST)。`created_at`は現在時刻であるべきだ。イベントの例を次に示す: 40 | 41 | ```jsonc 42 | { 43 | "kind": 22242, 44 | "tags": [ 45 | ["relay", "wss://relay.example.com/"], 46 | ["challenge", "challengestringhere"] 47 | ], 48 | // other fields... 49 | } 50 | ``` 51 | 52 | ### `OK` と `CLOSED` における機械可読な接頭辞 53 | 54 | このNIPは(クライアントによるイベント書き込みへの応答としての)`OK`と(クライアントが要求したが拒否された購読への応答としての)`CLOSED`の中で使用可能な2つの新しい接頭辞を定義する。 55 | 56 | - `"auth-required: "` - リレーがクエリまたはイベントの書き込みを受理するため必要であるにも関わらず、クライアントが`AUTH`を完了していない場合。 57 | - `"restricted: "` - クライアントは`AUTH`を正常に完了したが、認証に使用された鍵がまだリレーに許可されていないか、その認可を超過している場合。 58 | 59 | ## プロトコルの流れ 60 | 61 | リレーはいつでもチャレンジ文字列を含む`AUTH`メッセージをクライアントに送信してもよい。このチャレンジは接続期間中、または異なるチャレンジがリレーから送信されるまで有効である。クライアントはいかなる瞬間に対応する`AUTH`イベントを送信することを決定してもよく(MAY)、認証されたセッションは接続を通して有効である。 62 | 63 | ### `auth-required` in response to a `REQ` message 64 | 65 | 特定の処理のみ、例えば`REQ`への応答や`EVENT`書き込みの受理のみにリレーはクライアントに認証を求める可能性が高いことを加味すれば、これらが期待される一般的な流れとなる: 66 | 67 | ``` 68 | relay: ["AUTH", ""] 69 | client: ["REQ", "sub_1", {"kinds": [4]}] 70 | relay: ["CLOSED", "sub_1", "auth-required: we can't serve DMs to unauthenticated users"] 71 | client: ["AUTH", {"id": "abcdef...", ...}] 72 | relay: ["OK", "abcdef...", true, ""] 73 | client: ["REQ", "sub_1", {"kinds": [4]}] 74 | relay: ["EVENT", "sub_1", {...}] 75 | relay: ["EVENT", "sub_1", {...}] 76 | relay: ["EVENT", "sub_1", {...}] 77 | relay: ["EVENT", "sub_1", {...}] 78 | ... 79 | ``` 80 | 81 | このケースにおいて、リレーからの`AUTH`メッセージはクライアントが接続した直前に送信されてもよいし、`CLOSED`が送信される直前に送信されてもよい。唯一の要件は、`auth-required`な`CLOSED`メッセージに反応してクライアントが適切に振る舞えるよう、_リレーと関連づいたチャレンジを保存しておかなければならない_ ということである。 82 | 83 | ### `EVENT`メッセージに対する`auth-required` 84 | 85 | クライアントが`EVENT`をリレーに書き込みたいときにも同じ流れが有効である。ただし、この場合リレーは`CLOSED`メッセージの代わりに`OK`メッセージで応答する。 86 | 87 | ``` 88 | relay: ["AUTH", ""] 89 | client: ["EVENT", {"id": "012345...", ...}] 90 | relay: ["OK", "012345...", false, "auth-required: we only accept events from registered users"] 91 | client: ["AUTH", {"id": "abcdef...", ...}] 92 | relay: ["OK", "abcdef...", true, ""] 93 | client: ["EVENT", {"id": "012345...", ...}] 94 | relay: ["OK", "012345...", true, ""] 95 | ``` 96 | 97 | ## 署名されたイベントの検証 98 | 99 | `AUTH`メッセージを検証するため、リレーは以下を確認しなければならない: 100 | 101 | - その`kind`が`22242`である 102 | - そのイベントの`created_at`が現在時刻に近い (例: 10分以内) 103 | - その`"challenge"`タグの値が以前送ったチャレンジと合致する 104 | - その`"relay"`タグの値がリレーのURLと合致する 105 | - URL正規化技術が適用されている可能性がある。ほとんどの場合、ドメイン名が正しいかどうかを確認するだけで十分である。 106 | -------------------------------------------------------------------------------- /45.md: -------------------------------------------------------------------------------- 1 | NIP-45 2 | ====== 3 | 4 | Event Counts 5 | ------------ 6 | 7 | `draft` `optional` 8 | 9 | Relays may support the verb `COUNT`, which provides a mechanism for obtaining event counts. 10 | 11 | ## Motivation 12 | 13 | Some queries a client may want to execute against connected relays are prohibitively expensive, for example, in order to retrieve follower counts for a given pubkey, a client must query all kind-3 events referring to a given pubkey only to count them. The result may be cached, either by a client or by a separate indexing server as an alternative, but both options erode the decentralization of the network by creating a second-layer protocol on top of Nostr. 14 | 15 | ## Filters and return values 16 | 17 | This NIP defines the verb `COUNT`, which accepts a subscription id and filters as specified in [NIP 01](01.md) for the verb `REQ`. Multiple filters are OR'd together and aggregated into a single count result. 18 | 19 | ``` 20 | ["COUNT", , ...] 21 | ``` 22 | 23 | Counts are returned using a `COUNT` response in the form `{"count": }`. Relays may use probabilistic counts to reduce compute requirements. 24 | In case a relay uses probabilistic counts, it MAY indicate it in the response with `approximate` key i.e. `{"count": , "approximate": }`. 25 | 26 | ``` 27 | ["COUNT", , {"count": }] 28 | ``` 29 | 30 | Whenever the relay decides to refuse to fulfill the `COUNT` request, it MUST return a `CLOSED` message. 31 | 32 | ## Examples 33 | 34 | ### Followers count 35 | 36 | ``` 37 | ["COUNT", , {"kinds": [3], "#p": []}] 38 | ["COUNT", , {"count": 238}] 39 | ``` 40 | 41 | ### Count posts and reactions 42 | 43 | ``` 44 | ["COUNT", , {"kinds": [1, 7], "authors": []}] 45 | ["COUNT", , {"count": 5}] 46 | ``` 47 | 48 | ### Count posts approximately 49 | 50 | ``` 51 | ["COUNT", , {"kinds": [1]}] 52 | ["COUNT", , {"count": 93412452, "approximate": true}] 53 | ``` 54 | 55 | ### Relay refuses to count 56 | 57 | ``` 58 | ["COUNT", , {"kinds": [4], "authors": [], "#p": []}] 59 | ["CLOSED", , "auth-required: cannot count other people's DMs"] 60 | ``` 61 | -------------------------------------------------------------------------------- /48.md: -------------------------------------------------------------------------------- 1 | NIP-48 2 | ====== 3 | 4 | プロキシタグ 5 | ---------- 6 | 7 | `draft` `optional` 8 | 9 | ActivityPubなどの他のプロトコルからブリッジされたNostrイベントは、次の形式で`"proxy"`タグを含めることによってソースオブジェクトにリンクバックできる。 10 | 11 | ``` 12 | ["proxy", , ] 13 | ``` 14 | 15 | ここで: 16 | 17 | - `` はソースオブジェクトのIDである。 IDの形式はプロトコルによって異なる。 IDはプロトコルに関係なく、普遍的に一意である必要がある。 18 | - `` はプロトコルの名前である。 例 `"activitypub"`。 19 | 20 | クライアントはこの情報を使用して、他のプロトコルからブリッジされた重複したコンテンツを調整したり、ソースオブジェクトへのリンクを表示したりできる。 21 | 22 | プロキシタグはあらゆる種類のイベントに追加できる。これにより、イベントがNostrプロトコル上で発生したものではなく、Web上の他の場所で発生したことを示す。 23 | 24 | ### サポートされているプロトコル 25 | 26 | このリストは将来拡張される可能性がある。 27 | 28 | | Protocol | ID format | Example | 29 | | -------- | --------- | ------- | 30 | | `activitypub` | URL | `https://gleasonator.com/objects/9f524868-c1a0-4ee7-ad51-aaa23d68b526` | 31 | | `atproto` | AT URI | `at://did:plc:zhbjlbmir5dganqhueg7y4i3/app.bsky.feed.post/3jt5hlibeol2i` | 32 | | `rss` | URL with guid fragment | `https://soapbox.pub/rss/feed.xml#https%3A%2F%2Fsoapbox.pub%2Fblog%2Fmostr-fediverse-nostr-bridge` | 33 | | `web` | URL | `https://twitter.com/jack/status/20` | 34 | 35 | ### 例 36 | 37 | ActivityPub object: 38 | 39 | ```json 40 | { 41 | "kind": 1, 42 | "content": "I'm vegan btw", 43 | "tags": [ 44 | [ 45 | "proxy", 46 | "https://gleasonator.com/objects/8f6fac53-4f66-4c6e-ac7d-92e5e78c3e79", 47 | "activitypub" 48 | ] 49 | ], 50 | "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6", 51 | "created_at": 1691091365, 52 | "id": "55920b758b9c7b17854b6e3d44e6a02a83d1cb49e1227e75a30426dea94d4cb2", 53 | "sig": "a72f12c08f18e85d98fb92ae89e2fe63e48b8864c5e10fbdd5335f3c9f936397a6b0a7350efe251f8168b1601d7012d4a6d0ee6eec958067cf22a14f5a5ea579" 54 | } 55 | ``` 56 | 57 | ### 参考 58 | 59 | - [FEP-fffd: Proxy Objects](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md) 60 | - [Mostr bridge](https://mostr.pub/) -------------------------------------------------------------------------------- /49.md: -------------------------------------------------------------------------------- 1 | 2 | NIP-49 3 | ====== 4 | 5 | 秘密鍵暗号化 6 | ---------------------- 7 | 8 | `draft` `optional` 9 | 10 | このNIPは、クライアントがパスワードを使用してユーザーの秘密鍵を暗号化(および復号)できる方法を定義する。 11 | 12 | 対称暗号化鍵の導出 13 | ----------------------------------- 14 | 15 | PASSWORD = ユーザーから読み取る。パスワードは、他のコンピュータやクライアントで同じように入力できるよう、UnicodeをNFKC形式に正規化する必要がある。 16 | 17 | LOG\_N = ユーザーまたは実装者に、scryptのラウンド数として使用される2のべき乗を表す1バイト(e.g. 18は262,144を表す)を選択させる。数値が大きいほど、より多くの時間とより多くのメモリが必要になり、より強力な保護が提供される: 18 | 19 | | LOG_N | MEMORY REQUIRED | APPROX TIME ON FAST COMPUTER | 20 | |-------|-----------------|----------------------------- | 21 | | 16 | 64 MiB | 100 ms | 22 | | 18 | 256 MiB | | 23 | | 20 | 1 GiB | 2 seconds | 24 | | 21 | 2 GiB | | 25 | | 22 | 4 GiB | | 26 | 27 | SALT = 16ランダムバイト。 28 | 29 | SYMMETRIC_KEY = scrypt(password=PASSWORD, salt=SALT, log\_n=LOG\_N, r=8, p=1) 30 | 31 | 対称鍵の長さは32バイトである必要がある。 32 | 33 | この対称暗号化鍵は一時的なものであり、使用後はゼロにして破棄し、他の目的で保存したり再利用したりしてはいけない。 34 | 35 | 36 | 秘密鍵の暗号化 37 | ------------------------ 38 | 39 | 秘密鍵の暗号化プロセスは次のとおりである: 40 | 41 | PRIVATE\_KEY = 生の32バイトのユーザーのプライベート(秘密)secp256k1鍵(16進数またはbech32でエンコードされていない!) 42 | 43 | KEY\_SECURITY\_BYTE = 次のいずれか: 44 | 45 | * 0x00 - 鍵が安全に扱われていないことがわかっている場合(暗号化されずに保存されている、暗号化されていないカットアンドペーストなど) 46 | * 0x01 - 鍵が安全に扱われていないことがわかっていない場合(暗号化されずに保存されている、暗号化されていないカットアンドペーストなど) 47 | * 0x02 - クライアントがこのデータを追跡しない場合 48 | 49 | ASSOCIATED\_DATA = KEY\_SECURITY\_BYTE 50 | 51 | NONCE = 24バイトのランダムなナンス。 52 | 53 | CIPHERTEXT = XChaCha20-Poly1305( 54 | plaintext=PRIVATE\_KEY, 55 | associated_data=ASSOCIATED\_DATA, 56 | nonce=NONCE, 57 | key=SYMMETRIC\_KEY 58 | ) 59 | 60 | VERSION\_NUMBER = 0x02 61 | 62 | CIPHERTEXT_CONCATENATION = concat( 63 | VERSION\_NUMBER, 64 | LOG\_N, 65 | SALT, 66 | NONCE, 67 | ASSOCIATED\_DATA, 68 | CIPHERTEXT 69 | ) 70 | 71 | ENCRYPTED\_PRIVATE\_KEY = bech32_encode('ncryptsec', CIPHERTEXT\_CONCATENATION) 72 | 73 | bech32エンコード前の出力の長さは91バイトである必要がある。 74 | 75 | 復号プロセスはこの逆に行われる。 76 | 77 | 78 | テストデータ 79 | --------- 80 | 81 | ## パスワードのUnicode正規化 82 | 83 | 次のパスワード入力: "ÅΩẛ̣": 84 | - Unicodeコードポイント: U+212B U+2126 U+1E9B U+0323 85 | - UTF-8バイト: [0xE2, 0x84, 0xAB, 0xE2, 0x84, 0xA6, 0xE1, 0xBA, 0x9B, 0xCC, 0xA3] 86 | 87 | scryptで使用する前に、次のようにUnicode正規化されたNFKC形式に変換する必要がある: "ÅΩẛ̣": 88 | - Unicodeコードポイント: U+00C5 U+03A9 U+1E69 89 | - UTF-8バイト: [0xC3, 0x85, 0xCE, 0xA9, 0xE1, 0xB9, 0xA9] 90 | 91 | ## 暗号化 92 | 93 | 暗号化プロセスはランダムなノンスにより非決定的である。 94 | 95 | ## Decryption 96 | 97 | 以下の暗号化された秘密鍵: 98 | 99 | `ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p` 100 | 101 | パスワード = 'nostr'およびlog_n=16で復号すると、次の16進数でエンコードされた秘密鍵が生成される: 102 | 103 | `3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683` 104 | 105 | 議論 106 | ---------- 107 | 108 | ### 鍵の導出について 109 | 110 | パスワードは暗号鍵としては不十分である。暗号鍵として使用する前に、次の2つの作業をする必要がある: 111 | 112 | 1. 暗号化鍵は、対象暗号化アルゴリズムの仮定が有効であるような、一様にランダムなビット分布を持つようにパスワードから決定論的に作成する必要がある。そして 113 | 2. 多くのパスワードを試して復号する総当たり攻撃が大幅に阻止されるよう、低速で不可逆的なアルゴリズムをプロセスに挿入する必要がある。 114 | 115 | これらは、パスワードベースの鍵導出関数を使用して実現される。私たちはscryptを使用している。scryptは最大限メモリーハードであることが証明されており、2015年のコンテストでargon2が優勝したにもかかわらず、何人かの暗号学者がargon2よりも優れていると著者に示している。 116 | 117 | ### 対称暗号化アルゴリズムについて 118 | 119 | XChaCha20-Poly1305は通常、暗号学者によってAESよりも好まれており、米国政府とはあまり関係がない。これ(または「X」の付いていない初期のバリアント)は広く使用されており、TLSおよびOpenSSHで使用され、ほとんどの最新の暗号ライブラリで利用できる。 120 | 121 | 推奨事項 122 | --------- 123 | 124 | 攻撃者が多くの暗号化された秘密鍵を蓄積できると、鍵のクラッキングが容易になる可能性があるため、ユーザーがこれらの暗号化された秘密鍵をnostrに公開することは推奨できない。 125 | 126 | クライアントは、パスワードと秘密鍵のメモリを解放する前に、そのメモリをゼロにすることを推奨する。 127 | -------------------------------------------------------------------------------- /50.md: -------------------------------------------------------------------------------- 1 | NIP-50 2 | ====== 3 | 4 | 検索機能 5 | ----------------- 6 | 7 | `draft` `optional` 8 | 9 | ## 概要 10 | 11 | 多くのNostrのユースケースでは、タグやIDによる構造化されたクエリに加えて、何らかの形の一般的な検索機能が必要となる。 12 | 検索アルゴリズムの詳細はイベントのkindによって異なるはずなので、 13 | このNIPでは、そのようなクエリを実現するための一般的で拡張可能なフレームワークのみを記述する。 14 | 15 | ## `search` フィルタフィールド 16 | 17 | クライアントが送信する`REQ`メッセージに、新たに`search`フィールドが導入される: 18 | ```jsonc 19 | { 20 | // other fields on filter object... 21 | "search": 22 | } 23 | ``` 24 | `search`フィールドは、クエリを人間可読な形式で表現したものだ(例: "best nostr apps")。 25 | リレーは、できる限り最善の方法によってクエリを解釈し、それにマッチするイベントを返すべきだ(SHOULD)。 26 | リレーは、`content`イベントフィールドに対してマッチングを実行すべきであり(SHOULD)、 27 | その他のフィールドに対するマッチングも、特定のkindについて意味がある場合には実行してもよい(MAY)。 28 | 29 | 結果は通常の`.created_at`でなく、検索結果の品質 (実装によって定義される) の降順で返されるべきである (SHOULD)。 30 | `limit`フィルターはマッチングスコアでソートした後に適用するべきである (SHOULD)。 31 | クエリ文字列は、`key:value`ペア(コロンで区切られた2つの単語)を含むかもしれない。このようなものは拡張であり、リレーは 32 | サポートしていない拡張は無視すべきだ(SHOULD)。 33 | 34 | クライアントは、複数の検索フィルタを指定できる。例えば、`["REQ", "", { "search": "orange" }, { "kinds": [1, 2], "search": "purple" }]`のようにだ。クライアントは、 35 | `kinds`、`ids`などの他のフィルタフィールドを含めることで、検索結果を特定のイベントに限定できる。 36 | 37 | クライアントは、リレーが`search`フィルタをサポートしているかどうかを知るために、supported_nipsフィールドを使うべきだ(SHOULD)。 38 | クライアントは、このNIPをサポートしていないリレーからの余分な応答をフィルタリングするつもりであれば、`search`フィルタクエリをどんなリレーに送信してもよい(MAY)。 39 | 40 | リレーごとに実装詳細には差異がありうるので、それを埋めるため、 41 | クライアントは、このNIPをサポートしている複数のリレーをクエリするべきだ(SHOULD)。 42 | 43 | クライアントは、リレーの返したイベントが、クライアントのユースケースにしたがって指定されたクエリにマッチするかどうかを検証してよい(MAY)。 44 | また、精度が低いリレーに対してクエリするのを停止してもよい(MAY)。 45 | 46 | リレーは、もし何らかのスパムフィルタをサポートしているのであれば、デフォルトでスパムを検索結果から除外すべきだ(SHOULD)。 47 | 48 | ## 拡張 49 | 50 | リレーは以下のような拡張をサポートしてもよい (MAY): 51 | - `include:spam` - スパムフィルタをオフにする (デフォルトで有効な場合) 52 | - `domain:` - ドメインに一致する有効なnip05ドメインを持つユーザーのイベントのみを含める。 53 | - `language:` - 特定の言語のイベントのみを含める。 54 | - `sentiment:` - 特定の感情のイベントのみを含める。 55 | - `nsfw:` - nsfwイベントを含めるか、除外するか (デフォルト:true) 56 | -------------------------------------------------------------------------------- /53.md: -------------------------------------------------------------------------------- 1 | NIP-53 2 | ====== 3 | 4 | Live Activities 5 | --------------- 6 | 7 | `draft` `optional` 8 | 9 | Service providers want to offer live activities to the Nostr network in such a way that participants can easily log and query by clients. This NIP describes a general framework to advertise the involvement of pubkeys in such live activities. 10 | 11 | ## Concepts 12 | 13 | ### Live Event 14 | 15 | A special event with `kind:30311` "Live Event" is defined as an _addressable event_ of public `p` tags. Each `p` tag SHOULD have a **displayable** marker name for the current role (e.g. `Host`, `Speaker`, `Participant`) of the user in the event and the relay information MAY be empty. This event will be constantly updated as participants join and leave the activity. 16 | 17 | For example: 18 | 19 | ```jsonc 20 | { 21 | "kind": 30311, 22 | "tags": [ 23 | ["d", ""], 24 | ["title", ""], 25 | ["summary", ""], 26 | ["image", ""], 27 | ["t", "hashtag"] 28 | ["streaming", ""], 29 | ["recording", ""], // used to place the edited video once the activity is over 30 | ["starts", ""], 31 | ["ends", ""], 32 | ["status", ""], 33 | ["current_participants", ""], 34 | ["total_participants", ""], 35 | ["p", "91cf9..4e5ca", "wss://provider1.com/", "Host", ""], 36 | ["p", "14aeb..8dad4", "wss://provider2.com/nostr", "Speaker"], 37 | ["p", "612ae..e610f", "ws://provider3.com/ws", "Participant"], 38 | ["relays", "wss://one.com", "wss://two.com", /*...*/] 39 | ], 40 | "content": "", 41 | // other fields... 42 | } 43 | ``` 44 | 45 | A distinct `d` tag should be used for each activity. All other tags are optional. 46 | 47 | Providers SHOULD keep the participant list small (e.g. under 1000 users) and, when limits are reached, Providers SHOULD select which participants get named in the event. Clients should not expect a comprehensive list. Once the activity ends, the event can be deleted or updated to summarize the activity and provide async content (e.g. recording of the event). 48 | 49 | Clients are expected to subscribe to `kind:30311` events in general or for given follow lists and statuses. Clients MAY display participants' roles in activities as well as access points to join the activity. 50 | 51 | Live Activity management clients are expected to constantly update `kind:30311` during the event. Clients MAY choose to consider `status=live` events after 1hr without any update as `ended`. The `starts` and `ends` timestamp SHOULD be updated when the status changes to and from `live` 52 | 53 | The activity MUST be linked to using the [NIP-19](19.md) `naddr` code along with the `a` tag. 54 | 55 | ### Proof of Agreement to Participate 56 | 57 | Event owners can add proof as the 5th term in each `p` tag to clarify the participant's agreement in joining the event. The proof is a signed SHA256 of the complete `a` Tag of the event (`kind:pubkey:dTag`) by each `p`'s private key, encoded in hex. 58 | 59 | Clients MAY only display participants if the proof is available or MAY display participants as "invited" if the proof is not available. 60 | 61 | This feature is important to avoid malicious event owners adding large account holders to the event, without their knowledge, to lure their followers into the malicious owner's trap. 62 | 63 | ### Live Chat Message 64 | 65 | Event `kind:1311` is live chat's channel message. Clients MUST include the `a` tag of the activity with a `root` marker. Other Kind-1 tags such as `reply` and `mention` can also be used. 66 | 67 | ```jsonc 68 | { 69 | "kind": 1311, 70 | "tags": [ 71 | ["a", "30311::", "", "root"], 72 | ], 73 | "content": "Zaps to live streams is beautiful.", 74 | // other fields... 75 | } 76 | ``` 77 | 78 | ## Use Cases 79 | 80 | Common use cases include meeting rooms/workshops, watch-together activities, or event spaces, such as [zap.stream](https://zap.stream). 81 | 82 | ## Example 83 | 84 | ### Live Streaming 85 | 86 | ```json 87 | { 88 | "id": "57f28dbc264990e2c61e80a883862f7c114019804208b14da0bff81371e484d2", 89 | "pubkey": "1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec", 90 | "created_at": 1687182672, 91 | "kind": 30311, 92 | "tags": [ 93 | ["d", "demo-cf-stream"], 94 | ["title", "Adult Swim Metalocalypse"], 95 | ["summary", "Live stream from IPTV-ORG collection"], 96 | ["streaming", "https://adultswim-vodlive.cdn.turner.com/live/metalocalypse/stream.m3u8"], 97 | ["starts", "1687182672"], 98 | ["status", "live"], 99 | ["t", "animation"], 100 | ["t", "iptv"], 101 | ["image", "https://i.imgur.com/CaKq6Mt.png"] 102 | ], 103 | "content": "", 104 | "sig": "5bc7a60f5688effa5287244a24768cbe0dcd854436090abc3bef172f7f5db1410af4277508dbafc4f70a754a891c90ce3b966a7bc47e7c1eb71ff57640f3d389" 105 | } 106 | ``` 107 | 108 | ### Live Streaming chat message 109 | 110 | ```json 111 | { 112 | "id": "97aa81798ee6c5637f7b21a411f89e10244e195aa91cb341bf49f718e36c8188", 113 | "pubkey": "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24", 114 | "created_at": 1687286726, 115 | "kind": 1311, 116 | "tags": [ 117 | ["a", "30311:1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec:demo-cf-stream", "", "root"] 118 | ], 119 | "content": "Zaps to live streams is beautiful.", 120 | "sig": "997f62ddfc0827c121043074d50cfce7a528e978c575722748629a4137c45b75bdbc84170bedc723ef0a5a4c3daebf1fef2e93f5e2ddb98e5d685d022c30b622" 121 | } 122 | ``` 123 | -------------------------------------------------------------------------------- /54.md: -------------------------------------------------------------------------------- 1 | NIP-54 2 | ====== 3 | 4 | Wiki 5 | ---- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines `kind:30818` (an _addressable event_) for descriptions (or encyclopedia entries) of particular subjects, and it's expected that multiple people will write articles about the exact same subjects, with either small variations or completely independent content. 10 | 11 | Articles are identified by lowercase, normalized ascii `d` tags. 12 | 13 | ### Articles 14 | ```json 15 | { 16 | "content": "A wiki is a hypertext publication collaboratively edited and managed by its own audience.", 17 | "tags": [ 18 | ["d", "wiki"], 19 | ["title", "Wiki"], 20 | ] 21 | } 22 | ``` 23 | 24 | ### `d` tag normalization rules 25 | 26 | - Any non-letter character MUST be converted to a `-`. 27 | - All letters MUST be converted to lowercase. 28 | 29 | ### Content 30 | 31 | The `content` should be Asciidoc with two extra functionalities: **wikilinks** and **nostr:...** links. 32 | 33 | Unlike normal Asciidoc links `http://example.com[]` that link to external 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. 34 | 35 | Wikilinks can take these two forms: 36 | 37 | 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`; 38 | 2. `[[target page|see this]]` -- in this case it will link to the page `target-page`, but will be displayed as `see this`. 39 | 40 | `nostr:...` links, as per [NIP-21](21.md), should link to profiles or arbitrary Nostr events. Although it is not recommended to link to specific versions of articles -- instead the _wikilink_ syntax should be preferred, since it should be left to the reader and their client to decide what version of any given article they want to read. 41 | 42 | ### Optional extra tags 43 | 44 | - `title`: for when the display title should be different from the `d` tag. 45 | - `summary`: for display in lists. 46 | - `a` and `e`: for referencing the original event a wiki article was forked from. 47 | 48 | ### Merge Requests 49 | 50 | 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. 51 | 52 | [INSERT EVENT EXAMPLE] 53 | 54 | ### Redirects 55 | 56 | Event `kind:30819` is also defined to stand for "wiki redirects", i.e. if one thinks `Shell structure` should redirect to `Thin-shell structure` they can issue one of these events instead of replicating the content. These events can be used for automatically redirecting between articles on a client, but also for generating crowdsourced "disambiguation" pages ([common in Wikipedia](https://en.wikipedia.org/wiki/Help:Disambiguation)). 57 | 58 | [INSERT EVENT EXAMPLE] 59 | 60 | How to decide what article to display 61 | ------------------------------------- 62 | 63 | As there could be many articles for each given name, some kind of prioritization must be done by clients. Criteria for this should vary between users and clients, but some means that can be used are described below: 64 | 65 | ### Reactions 66 | 67 | [NIP-25](25.md) reactions are very simple and can be used to create a simple web-of-trust between wiki article writers and their content. While just counting a raw number of "likes" is unproductive, reacting to any wiki article event with a `+` can be interpreted as a recommendation for that article specifically and a partial recommendation of the author of that article. When 2 or 3-level deep recommendations are followed, suddenly a big part of all the articles may have some form of tagging. 68 | 69 | ### Relays 70 | 71 | [NIP-51](51.md) lists of relays can be created with the kind 10102 and then used by wiki clients in order to determine where to query articles first and to rank these differently in relation to other events fetched from other relays. 72 | 73 | ### Contact lists 74 | 75 | [NIP-02](02.md) contact lists can form the basis of a recommendation system that is then expanded with relay lists and reaction lists through nested queries. These lists form a good starting point only because they are so widespread. 76 | 77 | ### Wiki-related contact lists 78 | 79 | [NIP-51](51.md) lists can also be used to create a list of users that are trusted only in the context of wiki authorship or wiki curationship. 80 | 81 | Forks 82 | --------- 83 | Wiki-events can tag other wiki-events with a `fork` marker to specify that this event came from a different version. Both `a` and `e` tags SHOULD be used and have the `fork` marker applied, to identify the exact version it was forked from. 84 | 85 | Deference 86 | --------- 87 | Wiki-events can tag other wiki-events with a `defer` marker to indicate that it considers someone else's entry as a "better" version of itself. If using a `defer` marker both `a` and `e` tags SHOULD be used. 88 | 89 | This is a stronger signal of trust than a `+` reaction. 90 | 91 | This marker is useful when a user edits someone else's entry; if the original author includes the editor's changes and the editor doesn't want to keep/maintain an independent version, the `link` tag could effectively be a considered a "deletion" of the editor's version and putting that pubkey's WoT weight behind the original author's version. 92 | 93 | Why Asciidoc? 94 | ------------- 95 | 96 | Wikitext is [garbage](nostr:nevent1qqsqt0gcggry60n72uglhuhypdlmr2dm6swjj69jex5v530gcpazlzsprpmhxue69uhhyetvv9ujumn0wdmksetjv5hxxmmdqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsygpm7rrrljungc6q0tuh5hj7ue863q73qlheu4vywtzwhx42a7j9n5ueneex) and Markdown is not powerful enough (besides being too freeform and unspecified and prone to generate incompatibilities in the future). 97 | 98 | Asciidoc has a strict spec, multiple implementations in many languages, and support for features that are very much necessary in a wiki article, like _sidebars_, _tables_ (with rich markup inside cells), many levels of _headings_, _footnotes_, _superscript_ and _subscript_ markup and _description lists_. It is also arguably easier to read in its plaintext format than Markdown (and certainly much better than Wikitext). 99 | 100 | # Appendix 1: Merge requests 101 | Users can request other users to get their entries merged into someone else's entry by creating a `kind:818` event. 102 | 103 | ```json 104 | { 105 | "content": "I added information about how to make hot ice-creams", 106 | "kind": 818, 107 | "tags": [ 108 | [ "a", "30818::hot-ice-creams", "" ], 109 | [ "e", "", "" ], 110 | [ "p", "" ], 111 | [ "e", "", "", "source" ] 112 | ] 113 | } 114 | ``` 115 | 116 | `.content`: an optional explanation detailing why this merge is being requested. 117 | `a` tag: tag of the article which should be modified (i.e. the target of this merge request). 118 | `e` tag: optional version of the article in which this modifications is based 119 | `e` tag with `source` marker: the ID of the event that should be merged. This event id MUST be of a `kind:30818` as defined in this NIP. 120 | 121 | The destination-pubkey is the pubkey being requested to merge something into their article can create [[NIP-25]] reactions that tag the `kind:818` event with `+` or `-` 122 | -------------------------------------------------------------------------------- /56.md: -------------------------------------------------------------------------------- 1 | NIP-56 2 | ====== 3 | 4 | Reporting 5 | --------- 6 | 7 | `optional` 8 | 9 | A report is a `kind 1984` event that signals to users and relays that 10 | some referenced content is objectionable. The definition of objectionable is 11 | obviously subjective and all agents on the network (users, apps, relays, etc.) 12 | may consume and take action on them as they see fit. 13 | 14 | The `content` MAY contain additional information submitted by the entity 15 | reporting the content. 16 | 17 | Tags 18 | ---- 19 | 20 | The report event MUST include a `p` tag referencing the pubkey of the user you 21 | are reporting. 22 | 23 | If reporting a note, an `e` tag MUST also be included referencing the note id. 24 | 25 | A `report type` string MUST be included as the 3rd entry to the `e` or `p` tag 26 | being reported, which consists of the following report types: 27 | 28 | - `nudity` - depictions of nudity, porn, etc. 29 | - `malware` - virus, trojan horse, worm, robot, spyware, adware, back door, ransomware, rootkit, kidnapper, etc. 30 | - `profanity` - profanity, hateful speech, etc. 31 | - `illegal` - something which may be illegal in some jurisdiction 32 | - `spam` - spam 33 | - `impersonation` - someone pretending to be someone else 34 | - `other` - for reports that don't fit in the above categories 35 | 36 | Some report tags only make sense for profile reports, such as `impersonation` 37 | 38 | `l` and `L` tags MAY be also be used as defined in [NIP-32](32.md) to support 39 | further qualification and querying. 40 | 41 | Example events 42 | -------------- 43 | 44 | ```jsonc 45 | { 46 | "kind": 1984, 47 | "tags": [ 48 | ["p", , "nudity"], 49 | ["L", "social.nos.ontology"], 50 | ["l", "NS-nud", "social.nos.ontology"] 51 | ], 52 | "content": "", 53 | // other fields... 54 | } 55 | ``` 56 | 57 | ```jsonc 58 | { 59 | "kind": 1984, 60 | "tags": [ 61 | ["e", , "illegal"], 62 | ["p", ] 63 | ], 64 | "content": "He's insulting the king!", 65 | // other fields... 66 | } 67 | ``` 68 | 69 | ```jsonc 70 | { 71 | "kind": 1984, 72 | "tags": [ 73 | ["p", , "impersonation"] 74 | ], 75 | "content": "Profile is impersonating nostr:", 76 | // other fields... 77 | } 78 | ``` 79 | 80 | Client behavior 81 | --------------- 82 | 83 | Clients can use reports from friends to make moderation decisions if they 84 | choose to. For instance, if 3+ of your friends report a profile for `nudity`, 85 | clients can have an option to automatically blur photos from said account. 86 | 87 | 88 | Relay behavior 89 | -------------- 90 | 91 | It is not recommended that relays perform automatic moderation using reports, 92 | as they can be easily gamed. Admins could use reports from trusted moderators to 93 | takedown illegal or explicit content if the relay does not allow such things. 94 | -------------------------------------------------------------------------------- /58.md: -------------------------------------------------------------------------------- 1 | NIP-58 2 | ====== 3 | 4 | バッジ 5 | ------ 6 | 7 | `draft` `optional` 8 | 9 | 3つの特別なイベントがユーザープロフィール上のバッジを定義、授与、陳列するために 10 | 用いられる。 11 | 12 | 1. 「バッジ定義」イベントはバッジ発行者によって発行されるバッジ (例えば`bravery`) を一意に特定する値を持つ`d`タグを持つ、kind `30009`のアドレス指定可能 (addressable) イベントとして定義される。 13 | 2. 「バッジ授与」イベントは、それぞれバッジ発行者が授与したい公開鍵を指す1つ以上の`p`タグと、「バッジ定義」イベントを参照する1つの`a`タグを持つkind `8`イベントである。授与されたバッジは変更したり譲渡できない。 14 | 15 | 3. 「プロフィールバッジ」イベントはアドレス指定可能 (addressable) な、 16 | `profile_badges`値を持つ`d`タグを持つkind `30008`イベントである。 17 | プロフィールバッジはそれぞれ「バッジ定義」と陳列したいバッジのための「バッジ授与」を参照する`a`タグと`e`タグの組の順序付きリストを含む。 18 | 19 | ### バッジ定義イベント 20 | 21 | 以下のタグが存在しなければならない (MUST) 。 22 | 23 | - バッジの一意な名前を持つ`d`タグ。 24 | 25 | 以下のタグが存在してもよい (MAY) 。 26 | 27 | - バッジの短い名前を持つ`name`タグ。 28 | - バッジを表現する高解像度な画像のURLを値として持つ`image`タグ。2つ目の値は任意で、ピクセル単位で`width`x`height`のようにして画像の寸法を指定する。バッジの推奨寸法は1024x1024ピクセルである。 29 | - 画像のテキスト表現、バッジの意味、あるいは発行の理由を含む 30 | `description`タグ。 (MAY) 31 | - `image`タグが参照する画像のサムネイル版を指すURLを1つ目の値として持つ1つ以上の`thumb`タグ。2つ目の値は任意で、`width`x`height`ピクセルでサムネイル寸法を指定する。 32 | 33 | ### バッジ授与イベント 34 | 35 | 以下のタグが存在しなければならない (MUST) 。 36 | 37 | - kind `30009`のバッジ定義イベントを参照する`a`タグ。 38 | - それぞれが授与対象の公開鍵を参照する1つ以上の`p`タグ。 39 | 40 | ### プロフィールバッジイベント 41 | 42 | 1つの公開鍵に対して授与されるバッジの数は無制限である。プロフィールバッジイベントで 43 | 個々のユーザーが授与されたバッジの受け入れ・拒否、 44 | プロフィールに陳列するバッジの順序を選択できる。 45 | 46 | 以下のタグが存在しなければならない (MUST) 。 47 | 48 | - 一意な識別子`profile_badges`を持つ`d`タグ。 49 | 50 | 以下のタグが存在してもよい (MAY) 。 51 | 52 | - それぞれkind `30009`のバッジ定義とkind `8`のバッジ授与を参照する、連続する0個以上の`a`タグと`e`タグの順序付きペア。クライアントは 53 | `e`タグを含まない`a`タグやその逆を無視しなければならない (SHOULD) 。 `e`タグによって参照される 54 | バッジ授与イベントは同じ`a`タグを含むべきだ。 55 | 56 | ### 動機 57 | 58 | ユーザーは (これに限定されないが) 特定の目標やタスク、または大義に対する認識、感謝、参加、または評価としてバッジを授与されてもよい (MAY) 。 59 | 60 | ユーザーは、信頼できるバッジ発行者からの名声・悪名・認識・サポートなどを意味するバッジを選んで飾ってもよい (MAY) 。 61 | 62 | ### 推奨事項 63 | 64 | クライアントはユーザーに対する価値/特別性を保っていることを保証する目的でバッジ発行者 (公開鍵) をホワイトリストに登録してもよい (MAY) 。 65 | 66 | バッジ画像のアスペクト比は1:1で、1024x1024の高解像度画像が推奨される。 67 | 68 | バッジのサムネイル画像の推奨寸法は512x512 (xl) ・256x256 (l) ・64x64 (m) ・32x32 (s) ・16x16 (xs)である。 69 | 70 | クライアントはユーザーによってプロフィールバッジイベントで指定されたすべてのバッジを表示しなくてもよいし、バッジ画像とサムネイルをクライアントのテーマに合うものへ置き換えてもよい (MAY) 。 71 | 72 | クライアントはユーザーが選択したバッジの数と使えるスペースに応じて最も適切なバッジのサムネイルを表示するべきだ (SHOULD) 。クライアントはユーザーのアクション (クリック、タップ、ホバー) に応じてバッジの高解像度画像を表示するべきだ (SHOULD) 。 73 | 74 | ### バッジ定義イベントの例 75 | 76 | ```jsonc 77 | { 78 | "pubkey": "alice", 79 | "kind": 30009, 80 | "tags": [ 81 | ["d", "bravery"], 82 | ["name", "Medal of Bravery"], 83 | ["description", "Awarded to users demonstrating bravery"], 84 | ["image", "https://nostr.academy/awards/bravery.png", "1024x1024"], 85 | ["thumb", "https://nostr.academy/awards/bravery_256x256.png", "256x256"] 86 | ], 87 | // other fields... 88 | } 89 | ``` 90 | 91 | ### バッジ授与イベントの例 92 | 93 | ```jsonc 94 | { 95 | "id": "", 96 | "kind": 8, 97 | "pubkey": "alice", 98 | "tags": [ 99 | ["a", "30009:alice:bravery"], 100 | ["p", "bob", "wss://relay"], 101 | ["p", "charlie", "wss://relay"] 102 | ], 103 | // other fields... 104 | } 105 | ``` 106 | 107 | ### プロフィールバッジイベントの例 108 | 109 | Honorable Bob The Brave: 110 | ```jsonc 111 | { 112 | "kind": 30008, 113 | "pubkey": "bob", 114 | "tags": [ 115 | ["d", "profile_badges"], 116 | ["a", "30009:alice:bravery"], 117 | ["e", "", "wss://nostr.academy"], 118 | ["a", "30009:alice:honor"], 119 | ["e", "", "wss://nostr.academy"] 120 | ], 121 | // other fields... 122 | } 123 | ``` 124 | -------------------------------------------------------------------------------- /60.md: -------------------------------------------------------------------------------- 1 | # NIP-60 2 | ## Cashu Wallet 3 | `draft` `optional` 4 | 5 | This NIP defines the operations of a cashu-based wallet. 6 | 7 | A cashu wallet is a wallet which information is stored in relays to make it accessible across applications. 8 | 9 | The purpose of this NIP is: 10 | * ease-of-use: new users immediately are able to receive funds without creating accounts with other services. 11 | * interoperability: users' wallets follows them across applications. 12 | 13 | This NIP doesn't deal with users' *receiving* money from someone else, it's just to keep state of the user's wallet. 14 | 15 | # High-level flow 16 | 1. A user has a `kind:37375` event that represents a wallet. 17 | 2. A user has `kind:7375` events that represent the unspent proofs of the wallet. -- The proofs are encrypted with the user's private key. 18 | 3. A user has `kind:7376` events that represent the spending history of the wallet -- This history is for informational purposes only and is completely optional. 19 | 20 | ## Wallet Event 21 | ```jsonc 22 | { 23 | "kind": 37375, 24 | "content": nip44_encrypt([ 25 | [ "balance", "100", "sat" ], 26 | [ "privkey", "hexkey" ] // explained in NIP-61 27 | ]), 28 | "tags": [ 29 | [ "d", "my-wallet" ], 30 | [ "mint", "https://mint1" ], 31 | [ "mint", "https://mint2" ], 32 | [ "mint", "https://mint3" ], 33 | [ "name", "my shitposting wallet" ], 34 | [ "unit", "sat" ], 35 | [ "description", "a wallet for my day-to-day shitposting" ], 36 | [ "relay", "wss://relay1" ], 37 | [ "relay", "wss://relay2" ], 38 | ] 39 | } 40 | ``` 41 | 42 | The wallet event is a parameterized replaceable event `kind:37375`. 43 | 44 | Tags: 45 | * `d` - wallet ID. 46 | * `mint` - Mint(s) this wallet uses -- there MUST be one or more mint tags. 47 | * `relay` - Relays where the wallet and related events can be found. -- one ore more relays SHOULD be specified. If missing, clients should follow [[NIP-65]]. 48 | * `unit` - Base unit of the wallet (e.g. "sat", "usd", etc). 49 | * `name` - Optional human-readable name for the wallet. 50 | * `description` - Optional human-readable description of the wallet. 51 | * `balance` - Optional best-effort balance of the wallet that can serve as a placeholder while an accurate balance is computed from fetching all unspent proofs. 52 | * `privkey` - Private key used to unlock P2PK ecash. MUST be stored encrypted in the `.content` field. **This is a different private key exclusively used for the wallet, not associated in any way to the user's nostr private key** -- This is only used when receiving funds from others, described in NIP-61. 53 | 54 | Any tag, other than the `d` tag, can be [[NIP-44]] encrypted into the `.content` field. 55 | 56 | ### Deleting a wallet event 57 | Due to PRE being hard to delete, if a user wants to delete a wallet, they should empty the event and keep just the `d` identifier and add a `deleted` tag. 58 | 59 | ## Token Event 60 | Token events are used to record the unspent proofs that come from the mint. 61 | 62 | There can be multiple `kind:7375` events for the same mint, and multiple proofs inside each `kind:7375` event. 63 | 64 | ```jsonc 65 | { 66 | "kind": 7375, 67 | "content": nip44_encrypt({ 68 | "mint": "https://stablenut.umint.cash", 69 | "proofs": [ 70 | { 71 | "id": "005c2502034d4f12", 72 | "amount": 1, 73 | "secret": "z+zyxAVLRqN9lEjxuNPSyRJzEstbl69Jc1vtimvtkPg=", 74 | "C": "0241d98a8197ef238a192d47edf191a9de78b657308937b4f7dd0aa53beae72c46" 75 | } 76 | ] 77 | }), 78 | "tags": [ 79 | [ "a", "37375::my-wallet" ] 80 | ] 81 | } 82 | ``` 83 | 84 | `.content` is a [[NIP-44]] encrypted payload storing the mint and the unencoded proofs. 85 | * `a` an optional tag linking the token to a specific wallet. 86 | 87 | ### Spending proofs 88 | When one or more proofs of a token are spent, the token event should be [[NIP-09]]-deleted and, if some proofs are unspent from the same token event, a new token event should be created rolling over the unspent proofs and adding any change outputs to the new token event. 89 | 90 | ## Spending History Event 91 | Clients SHOULD publish `kind:7376` events to create a transaction history when their balance changes. 92 | 93 | ```jsonc 94 | { 95 | "kind": 7376, 96 | "content": nip44_encrypt([ 97 | [ "direction", "in" ], // in = received, out = sent 98 | [ "amount", "1", "sat" ], 99 | [ "e", "", "", "created" ], 100 | ]), 101 | "tags": [ 102 | [ "a", "37375::my-wallet" ], 103 | ] 104 | } 105 | ``` 106 | 107 | * `direction` - The direction of the transaction; `in` for received funds, `out` for sent funds. 108 | * `a` - The wallet the transaction is related to. 109 | 110 | Clients MUST add `e` tags to create references of destroyed and created token events along with the marker of the meaning of the tag: 111 | * `created` - A new token event was created. 112 | * `destroyed` - A token event was destroyed. 113 | * `redeemed` - A [[NIP-61]] nutzap was redeemed. 114 | 115 | All tags can be [[NIP-44]] encrypted. Clients SHOULD leave `e` tags with a `redeemed` marker unencrypted. 116 | 117 | Multiple `e` tags can be added to a `kind:7376` event. 118 | 119 | # Flow 120 | A client that wants to check for user's wallets information starts by fetching `kind:10019` events from the user's relays, if no event is found, it should fall back to using the user's [[NIP-65]] relays. 121 | 122 | ## Fetch wallet and token list 123 | From those relays, the client should fetch wallet and token events. 124 | 125 | `"kinds": [37375, 7375], "authors": [""]` 126 | 127 | ## Fetch proofs 128 | While the client is fetching (and perhaps validating) proofs it can use the optional `balance` tag of the wallet event to display a estimate of the balance of the wallet. 129 | 130 | ## Spending token 131 | If Alice spends 4 sats from this token event 132 | ```jsonconc 133 | { 134 | "kind": 7375, 135 | "id": "event-id-1", 136 | "content": nip44_encrypt({ 137 | "mint": "https://stablenut.umint.cash", 138 | "proofs": [ 139 | { "id": "1", "amount": 1 }, 140 | { "id": "2", "amount": 2 }, 141 | { "id": "3", "amount": 4 }, 142 | { "id": "4", "amount": 8 }, 143 | ] 144 | }), 145 | "tags": [ 146 | [ "a", "37375::my-wallet" ] 147 | ] 148 | } 149 | ``` 150 | 151 | Her client: 152 | * MUST roll over the unspent proofs: 153 | ```jsonconc 154 | { 155 | "kind": 7375, 156 | "id": "event-id-2", 157 | "content": nip44_encrypt({ 158 | "mint": "https://stablenut.umint.cash", 159 | "proofs": [ 160 | { "id": "1", "amount": 1 }, 161 | { "id": "2", "amount": 2 }, 162 | { "id": "4", "amount": 8 }, 163 | ] 164 | }), 165 | "tags": [ 166 | [ "a", "37375::my-wallet" ] 167 | ] 168 | } 169 | ``` 170 | * MUST delete event `event-id-1` 171 | * SHOULD create a `kind:7376` event to record the spend 172 | ```jsonconc 173 | { 174 | "kind": 7376, 175 | "content": nip44_encrypt([ 176 | [ "direction", "out" ], 177 | [ "amount", "4", "sats" ], 178 | [ "e", "", "", "destroyed" ], 179 | [ "e", "", "", "created" ], 180 | ]), 181 | "tags": [ 182 | [ "a", "37375::my-wallet" ], 183 | ] 184 | } 185 | ``` 186 | 187 | ## Redeeming a quote (optional) 188 | When creating a quote at a mint, an event can be used to keep the state of the quote ID, which will be used to check when the quote has been paid. These events should be created with an expiration tag [[NIP-40]] matching the expiration of the bolt11 received from the mint; this signals to relays when they can safely discard these events. 189 | 190 | Application developers are encouraged to use local state when possible and only publish this event when it makes sense in the context of their application. 191 | 192 | ```jsonc 193 | { 194 | "kind": 7374, 195 | "content": nip44_encrypt("quote-id"), 196 | "tags": [ 197 | [ "expiration", "" ], 198 | [ "mint", "" ], 199 | [ "a", "37375::my-wallet" ] 200 | ] 201 | } 202 | ``` 203 | 204 | ## Appendix 1: Validating proofs 205 | Clients can optionally validate proofs to make sure they are not working from an old state; this logic is left up to particular implementations to decide when and why to do it, but if some proofs are checked and deemed to have been spent, the client should delete the token and roll over any unspent proof. 206 | -------------------------------------------------------------------------------- /61.md: -------------------------------------------------------------------------------- 1 | # NIP-61: 2 | ## Nut Zaps 3 | 4 | A Nut Zap is a P2PK cashu token where the payment itself is the receipt. 5 | 6 | # High-level flow 7 | Alice wants to nutzap 1 sat to Bob because of an event `event-id-1` she liked. 8 | 9 | ## Alice nutzaps Bob 10 | 1. Alice fetches event `kind:10019` from Bob to see the mints Bob trusts. 11 | 2. She mints a token at that mint (or swaps some tokens she already had in that mint) p2pk-locked to the pubkey Bob has listed in his `kind:10019`. 12 | 3. She publishes a `kind:9321` event to the relays Bob indicated with the proofs she minted. 13 | 14 | ## Bob receives the nutzap 15 | 1. At some point, Bob's client fetches `kind:9321` events p-tagging him from his relays. 16 | 2. Bob's client swaps the token into his wallet. 17 | 18 | # Nutzap informational event 19 | ```jsonc 20 | { 21 | "kind": 10019, 22 | "tags": [ 23 | [ "relay", "wss://relay1" ], 24 | [ "relay", "wss://relay2" ], 25 | [ "mint", "https://mint1", "usd", "sat" ], 26 | [ "mint", "https://mint2", "sat" ], 27 | [ "pubkey", "" ] 28 | ] 29 | } 30 | ``` 31 | 32 | `kind:10019` is an event that is useful for others to know how to send money to the user. 33 | 34 | * `relay` - Relays where the user will be reading token events from. If a user wants to send money to the user, they should write to these relays. 35 | * `mint` - Mints the user is explicitly agreeing to use to receive funds on. Clients SHOULD not send money on mints not listed here or risk burning their money. Additional markers can be used to list the supported base units of the mint. 36 | * `pubkey` - Pubkey that SHOULD be used to P2PK-lock receiving nutzaps. If not present, clients SHOULD use the pubkey of the recipient. This is explained in Appendix 1. 37 | 38 | ## Nutzap event 39 | Event `kind:9321` is a nutzap event published by the sender, p-tagging the recipient. The outputs are P2PK-locked to the pubkey the recipient indicated in their `kind:10019` event or to the recipient pubkey if the `kind:10019` event doesn't have a explicit pubkey. 40 | 41 | Clients MUST prefix the pubkey they p2pk-lock with `"02"` (for nostr<>cashu pubkey compatibility). 42 | 43 | ```jsonc 44 | { 45 | kind: 9321, 46 | content: "Thanks for this great idea.", 47 | pubkey: "sender-pubkey", 48 | tags: [ 49 | [ "amount", "1" ], 50 | [ "unit", "sat" ], 51 | [ "proof", "{\"amount\":1,\"C\":\"02277c66191736eb72fce9d975d08e3191f8f96afb73ab1eec37e4465683066d3f\",\"id\":\"000a93d6f8a1d2c4\",\"secret\":\"[\\\"P2PK\\\",{\\\"nonce\\\":\\\"b00bdd0467b0090a25bdf2d2f0d45ac4e355c482c1418350f273a04fedaaee83\\\",\\\"data\\\":\\\"02eaee8939e3565e48cc62967e2fde9d8e2a4b3ec0081f29eceff5c64ef10ac1ed\\\"}]\"}" ], 52 | [ "u", "https://stablenut.umint.cash", ], 53 | [ "e", "", "" ], 54 | [ "p", "e9fbced3a42dcf551486650cc752ab354347dd413b307484e4fd1818ab53f991" ], // recipient of nut zap 55 | ] 56 | } 57 | ``` 58 | 59 | * `.content` is an optional comment for the nutzap 60 | * `amount` is a shorthand for the combined amount of all outputs. -- Clients SHOULD validate that the sum of the amounts in the outputs matches. 61 | * `unit` is the base unit of the amount. 62 | * `proof` is one ore more proofs p2pk-locked to the pubkey the recipient specified in their `kind:10019` event. 63 | * `u` is the mint the URL of the mint EXACTLY as specified by the recipient's `kind:10019`. 64 | * `e` zero or one event that is being nutzapped. 65 | * `p` exactly one pubkey, specifying the recipient of the nutzap. 66 | 67 | WIP: Clients SHOULD embed a DLEQ proof in the nutzap event to make it possible to verify nutzaps without talking to the mint. 68 | 69 | # Sending a nutzap 70 | 71 | * The sender fetches the recipient's `kind:10019`. 72 | * The sender mints/swaps ecash on one of the recipient's listed mints. 73 | * The sender p2pk locks to the recipient's specified pubkey in their 74 | 75 | # Receiving nutzaps 76 | 77 | Clients should REQ for nut zaps: 78 | * Filtering with `#u` for mints they expect to receive ecash from. 79 | * this is to prevent even interacting with mints the user hasn't explicitly signaled. 80 | * Filtering with `since` of the most recent `kind:7376` event the same user has created. 81 | * this can be used as a marker of the nut zaps that have already been swaped by the user -- clients might choose to use other kinds of markers, including internal state -- this is just a guidance of one possible approach. 82 | 83 | Clients MIGHT choose to use some kind of filtering (e.g. WoT) to ignore spam. 84 | 85 | `{ "kinds": [9321], "#p": "my-pubkey", "#u": [ "", ""], "since": }`. 86 | 87 | Upon receiving a new nut zap, the client should swap the tokens into a wallet the user controls, either a [[NIP-60]] wallet, their own LN wallet or anything else. 88 | 89 | ## Updating nutzap-redemption history 90 | When claiming a token the client SHOULD create a `kind:7376` event and `e` tag the original nut zap event. This is to record that this token has already been claimed (and shouldn't be attempted again) and as signaling to the recipient that the ecash has been redeemed. 91 | 92 | Multiple `kind:9321` events can be tagged in the same `kind:7376` event. 93 | 94 | ```jsonc 95 | { 96 | "kind": 7376, 97 | "content": nip44_encrypt([ 98 | [ "direction", "in" ], // in = received, out = sent 99 | [ "amount", "1", "sat" ], 100 | [ "e", "<7375-event-id>", "relay-hint", "created" ] // new token event that was created 101 | ]), 102 | "tags": [ 103 | [ "a", "37375::my-wallet" ], // an optional wallet tag 104 | [ "e", "<9321-event-id>", "relay-hint", "redeemed" ], // nutzap event that has been redeemed 105 | [ "p", "sender-pubkey" ] // pubkey of the author of the 9321 event (nutzap sender) 106 | ] 107 | } 108 | ``` 109 | 110 | Events that redeem a nutzap SHOULD be published to the sender's [[NIP-65]] relays. 111 | 112 | ## Verifying a Cashu Zap 113 | * Clients SHOULD check that the receiving user has issued a `kind:10019` tagging the mint where the cashu has been minted. 114 | * Clients SHOULD check that the token is locked to the pubkey the user has listed in their `kind:10019`. 115 | 116 | ## Final Considerations 117 | 118 | 1. Clients SHOULD guide their users to use NUT-11 (P2PK) compatible-mints in their `kind:10019` event to avoid receiving nut zaps anyone can spend 119 | 120 | 2. Clients SHOULD normalize and deduplicate mint URLs as described in NIP-65. 121 | 122 | 3. A nut zap MUST be sent to a mint the recipient has listed in their `kind:10019` event or to the NIP-65 relays of the recipient, failure to do so may result in the recipient donating the tokens to the mint since the recipient might never see the event. 123 | 124 | ## Appendix 1: Alternative P2PK pubkey 125 | Clients might not have access to the user's private key (i.e. NIP-07, NIP-46 signing) and, as such, the private key to sign cashu spends might not be available, which would make spending the P2PK incoming nutzaps impossible. 126 | 127 | For this scenarios clients can: 128 | 129 | * add a `pubkey` tag to the `kind:10019` (indicating which pubkey senders should P2PK to) 130 | * store the private key in the `kind:37375` event in the nip44-encrypted `content` field. 131 | 132 | This is to avoid depending on NIP-07/46 adaptations to sign cashu payloads. -------------------------------------------------------------------------------- /64.md: -------------------------------------------------------------------------------- 1 | NIP-64 2 | ====== 3 | 4 | Chess (Portable Game Notation) 5 | ------------------------------ 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines `kind:64` notes representing chess games in [PGN][pgn_specification] format, which can be read by humans and is also supported by most chess software. 10 | 11 | ## Note 12 | 13 | ### Content 14 | 15 | The `.content` of these notes is a string representing a [PGN-database][pgn_formal_syntax]. 16 | 17 | ### Notes 18 | 19 | ```jsonc 20 | { 21 | "kind": 64, 22 | "content": "1. e4 *", 23 | // other fields... 24 | } 25 | ``` 26 | 27 | ```jsonc 28 | { 29 | "kind": 64, 30 | "tags": [ 31 | ["alt", "Fischer vs. Spassky in Belgrade on 1992-11-04 (F/S Return Match, Round 29)"], 32 | // rest of tags... 33 | ], 34 | "content": "[Event \"F/S Return Match\"]\n[Site \"Belgrade, Serbia JUG\"]\n[Date \"1992.11.04\"]\n[Round \"29\"]\n[White \"Fischer, Robert J.\"]\n[Black \"Spassky, Boris V.\"]\n[Result \"1/2-1/2\"]\n\n1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} 3... a6\n4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7\n11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5\nNxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6\n23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5\nhxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5\n35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6\nNf2 42. g4 Bd3 43. Re6 1/2-1/2", 35 | // other fields... 36 | } 37 | ``` 38 | 39 | ## Client Behavior 40 | 41 | Clients SHOULD display the content represented as chessboard. 42 | 43 | Clients SHOULD publish PGN notes in ["export format"][pgn_export_format] ("strict mode", i.e. created by machines) but expect incoming notes to be in ["import format"][pgn_import_format] ("lax mode", i.e. created by humans). 44 | 45 | Clients SHOULD check whether the formatting is valid and all moves comply with chess rules. 46 | 47 | Clients MAY include additional tags (e.g. like [`"alt"`](https://github.com/nostr-protocol/nips/blob/master/31.md)) in order to represent the note to users of non-supporting clients. 48 | 49 | ## Relay Behavior 50 | 51 | Relays MAY validate PGN contents and reject invalid notes. 52 | 53 | 54 | ## Examples 55 | 56 | ```pgn 57 | // A game where nothing is known. Game still in progress, game abandoned, or result otherwise unknown. 58 | // Maybe players died before a move has been made. 59 | * 60 | ``` 61 | 62 | ```pgn 63 | 1. e4 * 64 | ``` 65 | 66 | ```pgn 67 | [White "Fischer, Robert J."] 68 | [Black "Spassky, Boris V."] 69 | 70 | 1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} * 71 | ``` 72 | 73 | ```pgn 74 | [Event "F/S Return Match"] 75 | [Site "Belgrade, Serbia JUG"] 76 | [Date "1992.11.04"] 77 | [Round "29"] 78 | [White "Fischer, Robert J."] 79 | [Black "Spassky, Boris V."] 80 | [Result "1/2-1/2"] 81 | 82 | 1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} 3... a6 83 | 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7 84 | 11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5 85 | Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6 86 | 23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5 87 | hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5 88 | 35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6 89 | Nf2 42. g4 Bd3 43. Re6 1/2-1/2 90 | ``` 91 | 92 | ```pgn 93 | [Event "Hourly HyperBullet Arena"] 94 | [Site "https://lichess.org/wxx4GldJ"] 95 | [Date "2017.04.01"] 96 | [White "T_LUKE"] 97 | [Black "decidement"] 98 | [Result "1-0"] 99 | [UTCDate "2017.04.01"] 100 | [UTCTime "11:56:14"] 101 | [WhiteElo "2047"] 102 | [BlackElo "1984"] 103 | [WhiteRatingDiff "+10"] 104 | [BlackRatingDiff "-7"] 105 | [Variant "Standard"] 106 | [TimeControl "30+0"] 107 | [ECO "B00"] 108 | [Termination "Abandoned"] 109 | 110 | 1. e4 1-0 111 | 112 | 113 | [Event "Hourly HyperBullet Arena"] 114 | [Site "https://lichess.org/rospUdSk"] 115 | [Date "2017.04.01"] 116 | [White "Bastel"] 117 | [Black "oslochess"] 118 | [Result "1-0"] 119 | [UTCDate "2017.04.01"] 120 | [UTCTime "11:55:56"] 121 | [WhiteElo "2212"] 122 | [BlackElo "2000"] 123 | [WhiteRatingDiff "+6"] 124 | [BlackRatingDiff "-4"] 125 | [Variant "Standard"] 126 | [TimeControl "30+0"] 127 | [ECO "A01"] 128 | [Termination "Normal"] 129 | 130 | 1. b3 d5 2. Bb2 c6 3. Nc3 Bf5 4. d4 Nf6 5. e3 Nbd7 6. f4 Bg6 7. Nf3 Bh5 8. Bd3 e6 9. O-O Be7 10. Qe1 O-O 11. Ne5 Bg6 12. Nxg6 hxg6 13. e4 dxe4 14. Nxe4 Nxe4 15. Bxe4 Nf6 16. c4 Bd6 17. Bc2 Qc7 18. f5 Be7 19. fxe6 fxe6 20. Qxe6+ Kh8 21. Qh3+ Kg8 22. Bxg6 Qd7 23. Qe3 Bd6 24. Bf5 Qe7 25. Be6+ Kh8 26. Qh3+ Nh7 27. Bf5 Rf6 28. Qxh7# 1-0 131 | ``` 132 | 133 | ## Resources 134 | - [PGN Specification][pgn_specification]: PGN (Portable Game Notation) specification 135 | - [PGN Specification Supplement](https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-spec-supplement.md): Addition for adding graphical elements, clock values, eval, ... 136 | - [PGN Formal Syntax][pgn_formal_syntax] 137 | - [PGN Seven Tag Roster][pgn_seven_tag_roster] 138 | - [PGN Import Format][pgn_import_format] 139 | - [PGN Export Format][pgn_export_format] 140 | - [lichess / pgn-viewer (GitHub)](https://github.com/lichess-org/pgn-viewer): PGN viewer widget, designed to be embedded in content pages 141 | 142 | [pgn_specification]: https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md 143 | [pgn_formal_syntax]: https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#18-formal-syntax 144 | [pgn_seven_tag_roster]: https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#811-seven-tag-roster 145 | [pgn_import_format]: https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#31-import-format-allows-for-manually-prepared-data 146 | [pgn_export_format]: https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#32-export-format-used-for-program-generated-output 147 | -------------------------------------------------------------------------------- /65.md: -------------------------------------------------------------------------------- 1 | NIP-65 2 | ====== 3 | 4 | リレーリストメタデータ 5 | ------------------- 6 | 7 | `draft` `optional` 8 | 9 | あるユーザのコンテンツを発見したり他人からの最新のコンテンツを受け取ったりするための推奨リレーの広告を目的とする、`kind:10002`を用いる上書き可能イベントを定義する。 10 | 11 | このイベントには、リレーURIと`read`または`write`マーカーを持つ`r`タグのリストを含めなければならない (MUST) 。`read`・`write` とマークされたリレーをそれぞれ読み出し (READ) ・書き込み (WRITE) リレーと呼ぶ。マーカーが省略された場合、そのリレーは両方の目的で利用される。 12 | 13 | `.content`は使用しない。 14 | 15 | ```jsonc 16 | { 17 | "kind": 10002, 18 | "tags": [ 19 | ["r", "wss://alicerelay.example.com"], 20 | ["r", "wss://brando-relay.com"], 21 | ["r", "wss://expensive-relay.example2.com", "write"], 22 | ["r", "wss://nostr-relay.example.com", "read"] 23 | ], 24 | "content": "", 25 | // その他のフィールド... 26 | } 27 | ``` 28 | 29 | このNIPは、 (`kind:3`スタイルのリレーリストのような) クライアントによるリレーの使われ方を設定する目的で設計されたリレーリストを完全に置き換えるものではない。クライアントは`kind:10002`のリレーリストを発見できない状況において、他のリレーリストを使用してもかまわない (MAY) 。 30 | 31 | ## 読み出し・書き込みリレーをいつ使用すべきか 32 | 33 | あるユーザ**から**発せられたイベントを探す際は、クライアントはそのユーザの`kind:10002`に含まれる書き込み (WRITE) リレーを使用すべきである (SHOULD) 。 34 | 35 | あるユーザ**に関する**イベント、すなわちそのユーザがタグ付けされているイベントを探す際は、クライアントはそのユーザの`kind:10002`に含まれる読み出し (READ) リレーを使用すべきである (SHOULD) 。 36 | 37 | イベントを送信する際は、クライアントは以下のようにすべきである (SHOULD) 。 38 | 39 | - イベント発行者の書き込み(WRITE)リレーにイベントを送信する 40 | - イベントにタグ付けされた各ユーザの読み出し(READ)リレーにイベントを送信する 41 | 42 | ## 動機 43 | 44 | ユーザごとに固定のリレーリストを用いる従来のモデルでは、有力なリレー運用者に権力が集中する。 45 | 46 | - ほとんどのユーザは自分の投稿を同一の非常に人気なリレーに送信する。これは投稿をより幅広い読者に届けるのが目的である。 47 | - 多くのユーザは、より多くのデータを得るためにたくさんのリレーからイベントを取得しているが、これはデータの重複という代償を伴う。 48 | - イベントはリレー間でコピーされている。多くの場合、たくさんの別々のリレーにコピーされる。 49 | 50 | このNIPにより、クライアントが各ユーザ個人の最新のリレーセットに対して直接接続できるようになり、人気のリレーにイベントを送信する必要がなくなる。 51 | 52 | ## 総論 53 | 54 | 1. クライアントは、`kind:10002`のリストを小さく (リレー2〜4個) 保つようユーザに案内すべきである (SHOULD) 。 55 | 56 | 2. クライアントは、可能な限りたくさんのリレーに作者の`kind:10002`のイベントを拡散するべきである (SHOULD) 。 57 | 58 | 3. `kind:10002`のイベントは、そのユーザが推奨するリレーを他人に広告することを主な目的として使用すべきである。そのユーザ自身のクライアントは、データの取得に用いるリレーを選択する際に他のヒューリスティクスを用いてもかまわない。 59 | 60 | 4. 最大限のプライバシーを保つために、ダイレクトメッセージは送信者の書き込み (WRITE) リレーと受信者の読み出し (READ) リレーのみに送信するべきである (SHOULD) 。 61 | 62 | 5. リレーが[NIP-11](11.md)ドキュメントでこのNIPのサポートを告知しているならば、それはそのリレーが、料金を支払っている顧客やホワイトリストで許可されたグループに限らず、幅広いユーザからの`kind:10002`のイベントを受け入れる意思があることを意味する。 63 | 64 | 6. クライアントは、[RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-6)に従ったリレーURIの正規化によって、接続の重複を排除するべきである (SHOULD) 。 65 | 66 | ## 関連記事 67 | - [アウトボックスモデル](https://mikedilger.com/gossip-model/) 68 | - [アウトボックスモデルって何?](https://habla.news/u/hodlbod@coracle.social/8YjqXm4SKY-TauwjOfLXS) 69 | -------------------------------------------------------------------------------- /68.md: -------------------------------------------------------------------------------- 1 | NIP-68 2 | ====== 3 | 4 | Picture-first feeds 5 | ------------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines event kind `20` for picture-first clients. Images must be self-contained. They are hosted externally and referenced using `imeta` tags. 10 | 11 | The idea is for this type of event to cater to Nostr clients resembling platforms like Instagram, Flickr, Snapchat, or 9GAG, where the picture itself takes center stage in the user experience. 12 | 13 | ## Picture Events 14 | 15 | Picture events contain a `title` tag and description in the `.content`. 16 | 17 | They may contain multiple images to be displayed as a single post. 18 | 19 | ```jsonc 20 | { 21 | "id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>, 22 | "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, 23 | "created_at": , 24 | "kind": 20, 25 | "content": "", 26 | "tags": [ 27 | ["title", ""], 28 | 29 | // Picture Data 30 | [ 31 | "imeta", 32 | "url https://nostr.build/i/my-image.jpg", 33 | "m image/jpeg", 34 | "blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$", 35 | "dim 3024x4032", 36 | "alt A scenic photo overlooking the coast of Costa Rica", 37 | "x ", 38 | "fallback https://nostrcheck.me/alt1.jpg", 39 | "fallback https://void.cat/alt1.jpg" 40 | ], 41 | [ 42 | "imeta", 43 | "url https://nostr.build/i/my-image2.jpg", 44 | "m image/jpeg", 45 | "blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$", 46 | "dim 3024x4032", 47 | "alt Another scenic photo overlooking the coast of Costa Rica", 48 | "x ", 49 | "fallback https://nostrcheck.me/alt2.jpg", 50 | "fallback https://void.cat/alt2.jpg", 51 | 52 | "annotate-user <32-bytes hex of a pubkey>::" // Tag users in specific locations in the picture 53 | ], 54 | 55 | ["content-warning", ""], // if NSFW 56 | 57 | // Tagged users 58 | ["p", "<32-bytes hex of a pubkey>", ""], 59 | ["p", "<32-bytes hex of a pubkey>", ""], 60 | 61 | // Specify the media type for filters to allow clients to filter by supported kinds 62 | ["m", "image/jpeg"], 63 | 64 | // Hashes of each image to make them queryable 65 | ["x", ""] 66 | 67 | // Hashtags 68 | ["t", ""], 69 | ["t", ""], 70 | 71 | // location 72 | ["location", ""], // city name, state, country 73 | ["g", ""], 74 | 75 | // When text is written in the image, add the tag to represent the language 76 | ["L", "ISO-639-1"], 77 | ["l", "en", "ISO-639-1"] 78 | ] 79 | } 80 | ``` 81 | 82 | The `imeta` tag `annotate-user` places a user link in the specific position in the image. 83 | 84 | Only the following media types are accepted: 85 | - `image/apng`: Animated Portable Network Graphics (APNG) 86 | - `image/avif`: AV1 Image File Format (AVIF) 87 | - `image/gif`: Graphics Interchange Format (GIF) 88 | - `image/jpeg`: Joint Photographic Expert Group image (JPEG) 89 | - `image/png`: Portable Network Graphics (PNG) 90 | - `image/webp`: Web Picture format (WEBP) 91 | 92 | Picture events might be used with [NIP-71](71.md)'s kind `34236` to display short vertical videos in the same feed. 93 | -------------------------------------------------------------------------------- /69.md: -------------------------------------------------------------------------------- 1 | # NIP-69 2 | 3 | ## Peer-to-peer Order events 4 | 5 | `draft` `optional` 6 | 7 | ## Abstract 8 | 9 | Peer-to-peer (P2P) platforms have seen an upturn in recent years, while having more and more options is positive, in the specific case of p2p, having several options contributes to the liquidity split, meaning sometimes there's not enough assets available for trading. If we combine all these individual solutions into one big pool of orders, it will make them much more competitive compared to centralized systems, where a single authority controls the liquidity. 10 | 11 | This NIP defines a simple standard for peer-to-peer order events, which enables the creation of a big liquidity pool for all p2p platforms participating. 12 | 13 | ## The event 14 | 15 | Events are [addressable events](https://github.com/nostr-protocol/nips/blob/master/01.md#kinds) and use `38383` as event kind, a p2p event look like this: 16 | 17 | ```json 18 | { 19 | "id": "84fad0d29cb3529d789faeff2033e88fe157a48e071c6a5d1619928289420e31", 20 | "pubkey": "dbe0b1be7aafd3cfba92d7463edbd4e33b2969f61bd554d37ac56f032e13355a", 21 | "created_at": 1702548701, 22 | "kind": 38383, 23 | "tags": [ 24 | ["d", "ede61c96-4c13-4519-bf3a-dcf7f1e9d842"], 25 | ["k", "sell"], 26 | ["f", "VES"], 27 | ["s", "pending"], 28 | ["amt", "0"], 29 | ["fa", "100"], 30 | ["pm", "face to face", "bank transfer"], 31 | ["premium", "1"], 32 | [ 33 | "rating", 34 | "{\"total_reviews\":1,\"total_rating\":3.0,\"last_rating\":3,\"max_rate\":5,\"min_rate\":1}" 35 | ], 36 | ["source", "https://t.me/p2plightning/xxxxxxx"], 37 | ["network", "mainnet"], 38 | ["layer", "lightning"], 39 | ["name", "Nakamoto"], 40 | ["g", ""], 41 | ["bond", "0"], 42 | ["expiration", "1719391096"], 43 | ["y", "lnp2pbot"], 44 | ["z", "order"] 45 | ], 46 | "content": "", 47 | "sig": "7e8fe1eb644f33ff51d8805c02a0e1a6d034e6234eac50ef7a7e0dac68a0414f7910366204fa8217086f90eddaa37ded71e61f736d1838e37c0b73f6a16c4af2" 48 | } 49 | ``` 50 | 51 | ## Tags 52 | 53 | - `d` < Order ID >: A unique identifier for the order. 54 | - `k` < Order type >: `sell` or `buy`. 55 | - `f` < Currency >: The asset being traded, using the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) standard. 56 | - `s` < Status >: `pending`, `canceled`, `in-progress`, `success`. 57 | - `amt` < Amount >: The amount of Bitcoin to be traded, the amount is defined in satoshis, if `0` means that the amount of satoshis will be obtained from a public API after the taker accepts the order. 58 | - `fa` < Fiat amount >: The fiat amount being traded, for range orders two values are expected, the minimum and maximum amount. 59 | - `pm` < Payment method >: The payment method used for the trade, if the order has multiple payment methods, they should be separated by a comma. 60 | - `premium` < Premium >: The percentage of the premium the maker is willing to pay. 61 | - `source` [Source]: The source of the order, it can be a URL that redirects to the order. 62 | - `rating` [Rating]: The rating of the maker, this document does not define how the rating is calculated, it's up to the platform to define it. 63 | - `network` < Network >: The network used for the trade, it can be `mainnet`, `testnet`, `signet`, etc. 64 | - `layer` < Layer >: The layer used for the trade, it can be `onchain`, `lightning`, `liquid`, etc. 65 | - `name` [Name]: The name of the maker. 66 | - `g` [Geohash]: The geohash of the operation, it can be useful in a face to face trade. 67 | - `bond` [Bond]: The bond amount, the bond is a security deposit that both parties must pay. 68 | - `expiration` < Expiration\>: The expiration date of the order ([NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md)). 69 | - `y` < Platform >: The platform that created the order. 70 | - `z` < Document >: `order`. 71 | 72 | Mandatory tags are enclosed with ``, optional tags are enclosed with `[tag]`. 73 | 74 | ## Implementations 75 | 76 | Currently implemented on the following platforms: 77 | 78 | - [Mostro](https://github.com/MostroP2P/mostro) 79 | - [@lnp2pBot](https://github.com/lnp2pBot/bot) 80 | - [Robosats](https://github.com/RoboSats/robosats/pull/1362) 81 | 82 | ## References 83 | 84 | - [Mostro protocol specification](https://mostro.network/protocol/) 85 | - [Messages specification for peer 2 peer NIP proposal](https://github.com/nostr-protocol/nips/blob/8250274a22f4882f621510df0054fd6167c10c9e/31001.md) 86 | - [n3xB](https://github.com/nobu-maeda/n3xb) 87 | -------------------------------------------------------------------------------- /70.md: -------------------------------------------------------------------------------- 1 | NIP-70 2 | ====== 3 | 4 | Protected Events 5 | ---------------- 6 | 7 | `draft` `optional` 8 | 9 | When the `"-"` tag is present, that means the event is "protected". 10 | 11 | A protected event is an event that can only be published to relays by its author. This is achieved by relays ensuring that the author is [authenticated](42.md) before publishing their own events or by just rejecting events with `["-"]` outright. 12 | 13 | The default behavior of a relay MUST be to reject any event that contains `["-"]`. 14 | 15 | Relays that want to accept such events MUST first require that the client perform the [NIP-42](42.md) `AUTH` flow and then check if the authenticated client has the same pubkey as the event being published and only accept the event in that case. 16 | 17 | ## The tag 18 | 19 | The tag is a simple tag with a single item: `["-"]`. It may be added to any event. 20 | 21 | ## Example flow 22 | 23 | - User `79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798` connects to relay `wss://example.com`: 24 | 25 | ```jsonc 26 | /* client: */ 27 | ["EVENT",{"id":"cb8feca582979d91fe90455867b34dbf4d65e4b86e86b3c68c368ca9f9eef6f2","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1707409439,"kind":1,"tags":[["-"]],"content":"hello members of the secret group","sig":"fa163f5cfb75d77d9b6269011872ee22b34fb48d23251e9879bb1e4ccbdd8aaaf4b6dc5f5084a65ef42c52fbcde8f3178bac3ba207de827ec513a6aa39fa684c"}] 28 | /* relay: */ 29 | ["AUTH", ""] 30 | ["OK", "cb8feca582979d91fe90455867b34dbf4d65e4b86e86b3c68c368ca9f9eef6f2", false, "auth-required: this event may only be published by its author"] 31 | /* client: */ 32 | ["AUTH", {}] 33 | ["EVENT",{"id":"cb8feca582979d91fe90455867b34dbf4d65e4b86e86b3c68c368ca9f9eef6f2","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1707409439,"kind":1,"tags":[["-"]],"content":"hello members of the secret group","sig":"fa163f5cfb75d77d9b6269011872ee22b34fb48d23251e9879bb1e4ccbdd8aaaf4b6dc5f5084a65ef42c52fbcde8f3178bac3ba207de827ec513a6aa39fa684c"}] 34 | ["OK", "cb8feca582979d91fe90455867b34dbf4d65e4b86e86b3c68c368ca9f9eef6f2", true, ""] 35 | ``` 36 | 37 | ## Why 38 | 39 | There are multiple circumstances in which it would be beneficial to prevent the unlimited spreading of an event through all relays imaginable and restrict some to only a certain demographic or to a semi-closed community relay. Even when the information is public it may make sense to keep it compartimentalized across different relays. 40 | 41 | It's also possible to create closed access feeds with this when the publisher has some relationship with the relay and trusts the relay to not release their published events to anyone. 42 | 43 | Even though it's ultimately impossible to restrict the spread of information on the internet (for example, one of the members of the closed group may want to take an event intended to be restricted and republish it to other relays), most relays would be happy to not facilitate the acts of these so-called "pirates", in respect to the original decision of the author and therefore gladly reject these republish acts if given the means to. 44 | 45 | This NIP gives these authors and relays the means to clearly signal when a given event is not intended to be republished by third parties. 46 | -------------------------------------------------------------------------------- /71.md: -------------------------------------------------------------------------------- 1 | NIP-71 2 | ====== 3 | 4 | Video Events 5 | ------------ 6 | 7 | `draft` `optional` 8 | 9 | This specification defines video events representing a dedicated post of externally hosted content. These video events are _addressable_ and delete-requestable per [NIP-09](09.md). 10 | 11 | Unlike a `kind 1` event with a video attached, Video Events are meant to contain all additional metadata concerning the subject media and to be surfaced in video-specific clients rather than general micro-blogging clients. The thought is for events of this kind to be referenced in a Netflix, YouTube, or TikTok like nostr client where the video itself is at the center of the experience. 12 | 13 | ## Video Events 14 | 15 | There are two types of video events represented by different kinds: horizontal and vertical video events. This is meant to allow clients to cater to each as the viewing experience for horizontal (landscape) videos is often different than that of vertical (portrait) videos (Stories, Reels, Shorts, etc). 16 | 17 | #### Format 18 | 19 | The format uses an _addressable event_ kind `34235` for horizontal videos and `34236` for vertical videos. 20 | 21 | The `.content` of these events is a summary or description on the video content. 22 | 23 | The primary source of video information is the `imeta` tags which is defined in [NIP-92](92.md) 24 | 25 | Each `imeta` tag can be used to specify a variant of the video by the `dim` & `m` properties. 26 | 27 | Example: 28 | ```json 29 | [ 30 | ["imeta", 31 | "dim 1920x1080", 32 | "url https://myvideo.com/1080/12345.mp4", 33 | "x 3093509d1e0bc604ff60cb9286f4cd7c781553bc8991937befaacfdc28ec5cdc", 34 | "m video/mp4", 35 | "image https://myvideo.com/1080/12345.jpg", 36 | "image https://myotherserver.com/1080/12345.jpg", 37 | "fallback https://myotherserver.com/1080/12345.mp4", 38 | "fallback https://andanotherserver.com/1080/12345.mp4", 39 | "service nip96", 40 | ], 41 | ["imeta", 42 | "dim 1280x720", 43 | "url https://myvideo.com/720/12345.mp4", 44 | "x e1d4f808dae475ed32fb23ce52ef8ac82e3cc760702fca10d62d382d2da3697d", 45 | "m video/mp4", 46 | "image https://myvideo.com/720/12345.jpg", 47 | "image https://myotherserver.com/720/12345.jpg", 48 | "fallback https://myotherserver.com/720/12345.mp4", 49 | "fallback https://andanotherserver.com/720/12345.mp4", 50 | "service nip96", 51 | ], 52 | ["imeta", 53 | "dim 1280x720", 54 | "url https://myvideo.com/720/12345.m3u8", 55 | "x 704e720af2697f5d6a198ad377789d462054b6e8d790f8a3903afbc1e044014f", 56 | "m application/x-mpegURL", 57 | "image https://myvideo.com/720/12345.jpg", 58 | "image https://myotherserver.com/720/12345.jpg", 59 | "fallback https://myotherserver.com/720/12345.m3u8", 60 | "fallback https://andanotherserver.com/720/12345.m3u8", 61 | "service nip96", 62 | ], 63 | ] 64 | ``` 65 | 66 | Where `url` is the primary server url and `fallback` are other servers hosting the same file, both `url` and `fallback` should be weighted equally and clients are recommended to use any of the provided video urls. 67 | 68 | The `image` tag contains a preview image (at the same resolution). Multiple `image` tags may be used to specify fallback copies in the same way `fallback` is used for `url`. 69 | 70 | Additionally `service nip96` may be included to allow clients to search the authors NIP-96 server list to find the file using the hash. 71 | 72 | ### Other tags: 73 | * `title` (required) title of the video 74 | * `published_at`, for the timestamp in unix seconds (stringified) of the first time the video was published 75 | * `duration` (optional) video duration in seconds 76 | * `text-track` (optional, repeated) link to WebVTT file for video, type of supplementary information (captions/subtitles/chapters/metadata), optional language code 77 | * `content-warning` (optional) warning about content of NSFW video 78 | * `alt` (optional) description for accessibility 79 | * `segment` (optional, repeated) start timestamp in format `HH:MM:SS.sss`, end timestamp in format `HH:MM:SS.sss`, chapter/segment title, chapter thumbnail-url 80 | * `t` (optional, repeated) hashtag to categorize video 81 | * `p` (optional, repeated) 32-bytes hex pubkey of a participant in the video, optional recommended relay URL 82 | * `r` (optional, repeated) references / links to web pages 83 | 84 | ```jsonc 85 | { 86 | "id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>, 87 | "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, 88 | "created_at": , 89 | "kind": 34235 | 34236, 90 | "content": "", 91 | "tags": [ 92 | ["d", ""], 93 | 94 | ["title", ""], 95 | ["published_at", "<unix timestamp>"], 96 | ["alt", <description>], 97 | 98 | // Video Data 99 | ["imeta", 100 | "dim 1920x1080", 101 | "url https://myvideo.com/1080/12345.mp4", 102 | "x 3093509d1e0bc604ff60cb9286f4cd7c781553bc8991937befaacfdc28ec5cdc", 103 | "m video/mp4", 104 | "image https://myvideo.com/1080/12345.jpg", 105 | "image https://myotherserver.com/1080/12345.jpg", 106 | "fallback https://myotherserver.com/1080/12345.mp4", 107 | "fallback https://andanotherserver.com/1080/12345.mp4", 108 | "service nip96", 109 | ], 110 | 111 | ["duration", <duration of video in seconds>], 112 | ["text-track", "<encoded `kind 6000` event>", "<recommended relay urls>"], 113 | ["content-warning", "<reason>"], 114 | ["segment", <start>, <end>, "<title>", "<thumbnail URL>"], 115 | 116 | // Participants 117 | ["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>"], 118 | ["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>"], 119 | 120 | // Hashtags 121 | ["t", "<tag>"], 122 | ["t", "<tag>"], 123 | 124 | // Reference links 125 | ["r", "<url>"], 126 | ["r", "<url>"] 127 | ] 128 | } 129 | ``` -------------------------------------------------------------------------------- /72.md: -------------------------------------------------------------------------------- 1 | NIP-72 2 | ====== 3 | 4 | Moderated Communities (Reddit Style) 5 | ------------------------------------ 6 | 7 | `draft` `optional` 8 | 9 | The goal of this NIP is to enable public communities. It defines the replaceable event `kind:34550` to define the community and the current list of moderators/administrators. Users that want to post into the community, simply tag any Nostr event with the community's `a` tag. Moderators may issue an approval event `kind:4550`. 10 | 11 | # Community Definition 12 | 13 | `Kind:34550` SHOULD include any field that helps define the community and the set of moderators. `relay` tags MAY be used to describe the preferred relay to download requests and approvals. A community definition event's `d` tag MAY double as its name, but if a `name` tag is provided, it SHOULD be displayed instead of the `d` tag. 14 | 15 | ```jsonc 16 | { 17 | "created_at": <Unix timestamp in seconds>, 18 | "kind": 34550, 19 | "tags": [ 20 | ["d", "<community-d-identifier>"], 21 | ["name", "<Community name>"], 22 | ["description", "<Community description>"], 23 | ["image", "<Community image url>", "<Width>x<Height>"], 24 | 25 | //.. other tags relevant to defining the community 26 | 27 | // moderators 28 | ["p", "<32-bytes hex of a pubkey1>", "<optional recommended relay URL>", "moderator"], 29 | ["p", "<32-bytes hex of a pubkey2>", "<optional recommended relay URL>", "moderator"], 30 | ["p", "<32-bytes hex of a pubkey3>", "<optional recommended relay URL>", "moderator"], 31 | 32 | // relays used by the community (w/optional marker) 33 | ["relay", "<relay hosting author kind 0>", "author"], 34 | ["relay", "<relay where to send and receive requests>", "requests"], 35 | ["relay", "<relay where to send and receive approvals>", "approvals"], 36 | ["relay", "<relay where to post requests to and fetch approvals from>"] 37 | ], 38 | // other fields... 39 | } 40 | ``` 41 | 42 | # Posting to a community 43 | 44 | Any Nostr event can be posted to a community. Clients MUST add one or more community `a` tags, each with a recommended relay. 45 | 46 | ```jsonc 47 | { 48 | "kind": 1, 49 | "tags": [ 50 | ["a", "34550:<community event author pubkey>:<community-d-identifier>", "<optional-relay-url>"], 51 | ], 52 | "content": "hello world", 53 | // other fields... 54 | } 55 | ``` 56 | 57 | # Moderation 58 | 59 | Anyone may issue an approval event to express their opinion that a post is appropriate for a community. Clients MAY choose which approval events to honor, but SHOULD at least use ones published by the group's defined moderators. 60 | 61 | An approval event MUST include one or more community `a` tags, an `e` or `a` tag pointing to the post, and the `p` tag of the author of the post (for approval notifications). `a` tag prefixes can be used to disambiguate between community and replaceable event pointers (community `a` tags always begin with `34550`). 62 | 63 | The event SHOULD also include the JSON-stringified `post request` event inside the `.content`, and a `k` tag with the original post's event kind to allow filtering of approved posts by kind. 64 | 65 | Moderators MAY request deletion of their approval of a post at any time using [NIP-09 event deletion requests](09.md). 66 | 67 | ```jsonc 68 | { 69 | "pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>", 70 | "kind": 4550, 71 | "tags": [ 72 | ["a", "34550:<event-author-pubkey>:<community-d-identifier>", "<optional-relay-url>"], 73 | ["e", "<post-id>", "<optional-relay-url>"], 74 | ["p", "<port-author-pubkey>", "<optional-relay-url>"], 75 | ["k", "<post-request-kind>"] 76 | ], 77 | "content": "<the full approved event, JSON-encoded>", 78 | // other fields... 79 | } 80 | ``` 81 | 82 | It's recommended that multiple moderators approve posts to avoid deleting them from the community when a moderator is removed from the owner's list. In case the full list of moderators must be rotated, the new moderator set must sign new approvals for posts in the past or the community will restart. The owner can also periodically copy and re-sign of each moderator's approval events to make sure posts don't disappear with moderators. 83 | 84 | Approvals of replaceable events can be created in three ways: 85 | 86 | 1. By tagging the replaceable event as an `e` tag if moderators want to approve each individual change to the replaceable event 87 | 2. By tagging the replaceable event as an `a` tag if the moderator authorizes the replaceable event author to make changes without additional approvals and 88 | 3. By tagging the replaceable event with both its `e` and `a` tag which empowers clients to display the original and updated versions of the event, with appropriate remarks in the UI. 89 | 90 | Since relays are instructed to delete old versions of a replaceable event, the `content` of an approval using an `e` tag MUST have the specific version of the event or clients might not be able to find that version of the content anywhere. 91 | 92 | Clients SHOULD evaluate any non-`34550:*` `a` tag as posts to be approved for all `34550:*` `a` tags. 93 | 94 | # Cross-posting 95 | 96 | Clients MAY support cross-posting between communities by posting a NIP 18 `kind 6` or `kind 16` repost to one or more communities using `a` tags as described above. The `content` of the repost MUST be the original event, not the approval event. 97 | -------------------------------------------------------------------------------- /73.md: -------------------------------------------------------------------------------- 1 | NIP-73 2 | ====== 3 | 4 | External Content IDs 5 | -------------------- 6 | 7 | `draft` `optional` 8 | 9 | There are certain established global content identifiers such as [Book ISBNs](https://en.wikipedia.org/wiki/ISBN), [Podcast GUIDs](https://podcastnamespace.org/tag/guid), and [Movie ISANs](https://en.wikipedia.org/wiki/International_Standard_Audiovisual_Number) that are useful to reference in nostr events so that clients can query all the events assosiated with these ids. 10 | 11 | 12 | `i` tags are used for referencing these external content ids, with `k` tags representing the external content id kind so that clients can query all the events for a specific kind. 13 | 14 | ## Supported IDs 15 | 16 | | Type | `i` tag | `k` tag | 17 | |- | - | - | 18 | | URLs | "`<URL, normalized, no fragment>`" | "`<scheme-host, normalized>`" | 19 | | Hashtags | "#`<topic, lowercase>`" | "#" | 20 | | Geohashes| "geo:`<geohash, lowercase>`" | "geo" | 21 | | Books | "isbn:`<id, without hyphens>`" | "isbn" | 22 | | Podcast Feeds | "podcast:guid:`<guid>`" | "podcast:guid" | 23 | | Podcast Episodes | "podcast:item:guid:`<guid>`" | "podcast:item:guid" | 24 | | Podcast Publishers | "podcast:publisher:guid:`<guid>`" | "podcast:publisher:guid" | 25 | | Movies | "isan:`<id, without version part>`" | "isan" | 26 | | Papers | "doi:`<id, lowercase>`" | "doi" | 27 | 28 | --- 29 | 30 | ## Examples 31 | 32 | ### Books: 33 | 34 | - Book ISBN: `["i", "isbn:9780765382030"]` - https://isbnsearch.org/isbn/9780765382030 35 | 36 | Book ISBNs MUST be referenced _**without hyphens**_ as many book search APIs return the ISBNs without hyphens. Removing hypens from ISBNs is trivial, whereas adding the hyphens back in is non-trivial requiring a library. 37 | 38 | ### Podcasts: 39 | 40 | - Podcast RSS Feed GUID: `["i", "podcast:guid:c90e609a-df1e-596a-bd5e-57bcc8aad6cc"]` - https://podcastindex.org/podcast/c90e609a-df1e-596a-bd5e-57bcc8aad6cc 41 | - Podcast RSS Item GUID: `["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f"]` 42 | - Podcast RSS Publisher GUID: `["i", "podcast:publisher:guid:18bcbf10-6701-4ffb-b255-bc057390d738"]` 43 | 44 | ### Movies: 45 | 46 | - Movie ISAN: `["i", "isan:0000-0000-401A-0000-7"]` - https://web.isan.org/public/en/isan/0000-0000-401A-0000-7 47 | 48 | Movie ISANs SHOULD be referenced _**without the version part**_ as the versions / edits of movies are not relevant. More info on ISAN parts here - https://support.isan.org/hc/en-us/articles/360002783131-Records-relations-and-hierarchies-in-the-ISAN-Registry 49 | 50 | --- 51 | 52 | ### Optional URL Hints 53 | 54 | Each `i` tag MAY have a url hint as the second argument to redirect people to a website if the client isn't opinionated about how to interpret the id: 55 | 56 | `["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg]` 57 | 58 | `["i", "isan:0000-0000-401A-0000-7", https://www.imdb.com/title/tt0120737]` 59 | 60 | 61 | -------------------------------------------------------------------------------- /75.md: -------------------------------------------------------------------------------- 1 | NIP-75 2 | ====== 3 | 4 | Zap Goals 5 | --------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines an event for creating fundraising goals. Users can contribute funds towards the goal by zapping the goal event. 10 | 11 | ## Nostr Event 12 | 13 | A `kind:9041` event is used. 14 | 15 | The `.content` contains a human-readable description of the goal. 16 | 17 | The following tags are defined as REQUIRED. 18 | 19 | - `amount` - target amount in milisats. 20 | - `relays` - a list of relays the zaps to this goal will be sent to and tallied from. 21 | 22 | Example event: 23 | 24 | ```jsonc 25 | { 26 | "kind": 9041, 27 | "tags": [ 28 | ["relays", "wss://alicerelay.example.com", "wss://bobrelay.example.com", /*...*/], 29 | ["amount", "210000"], 30 | ], 31 | "content": "Nostrasia travel expenses", 32 | // other fields... 33 | } 34 | ``` 35 | 36 | The following tags are OPTIONAL. 37 | 38 | - `closed_at` - timestamp for determining which zaps are included in the tally. Zap receipts published after the `closed_at` timestamp SHOULD NOT count towards the goal progress. 39 | - `image` - an image for the goal 40 | - `summary` - a brief description 41 | 42 | ```jsonc 43 | { 44 | "kind": 9041, 45 | "tags": [ 46 | ["relays", "wss://alicerelay.example.com", "wss://bobrelay.example.com", /*...*/], 47 | ["amount", "210000"], 48 | ["closed_at", "<unix timestamp in seconds>"], 49 | ["image", "<image URL>"], 50 | ["summary", "<description of the goal>"], 51 | ], 52 | "content": "Nostrasia travel expenses", 53 | // other fields... 54 | } 55 | ``` 56 | 57 | The goal MAY include an `r` or `a` tag linking to a URL or addressable event. 58 | 59 | The goal MAY include multiple beneficiary pubkeys by specifying [`zap` tags](57.md#appendix-g-zap-tag-on-other-events). 60 | 61 | Addressable events can link to a goal by using a `goal` tag specifying the event id and an optional relay hint. 62 | 63 | ```jsonc 64 | { 65 | "kind": 3xxxx, 66 | "tags": [ 67 | ["goal", "<event id>", "<Relay URL (optional)>"], 68 | // rest of tags... 69 | ], 70 | // other fields... 71 | } 72 | ``` 73 | 74 | ## Client behavior 75 | 76 | Clients MAY display funding goals on user profiles. 77 | 78 | When zapping a goal event, clients MUST include the relays in the `relays` tag of the goal event in the zap request `relays` tag. 79 | 80 | When zapping an addressable event with a `goal` tag, clients SHOULD tag the goal event id in the `e` tag of the zap request. 81 | 82 | ## Use cases 83 | 84 | - Fundraising clients 85 | - Adding funding goals to events such as long form posts, badges or live streams 86 | -------------------------------------------------------------------------------- /78.md: -------------------------------------------------------------------------------- 1 | NIP-78 2 | ====== 3 | 4 | Arbitrary custom app data 5 | ------------------------- 6 | 7 | `draft` `optional` 8 | 9 | The goal of this NIP is to enable [remoteStorage](https://remotestorage.io/)-like capabilities for custom applications that do not care about interoperability. 10 | 11 | Even though interoperability is great, some apps do not want or do not need interoperability, and it wouldn't make sense for them. Yet Nostr can still serve as a generalized data storage for these apps in a "bring your own database" way, for example: a user would open an app and somehow input their preferred relay for storage, which would then enable these apps to store application-specific data there. 12 | 13 | ## Nostr event 14 | 15 | This NIP specifies the use of event kind `30078` (an _addressable_ event) with a `d` tag containing some reference to the app name and context -- or any other arbitrary string. `content` and other `tags` can be anything or in any format. 16 | 17 | ## Some use cases 18 | 19 | - User personal settings on Nostr clients (and other apps unrelated to Nostr) 20 | - A way for client developers to propagate dynamic parameters to users without these having to update 21 | - Personal private data generated by apps that have nothing to do with Nostr, but allow users to use Nostr relays as their personal database 22 | -------------------------------------------------------------------------------- /7D.md: -------------------------------------------------------------------------------- 1 | NIP-7D 2 | ====== 3 | 4 | Threads 5 | ------- 6 | 7 | `draft` `optional` 8 | 9 | A thread is a `kind 11` event. Threads SHOULD include a `subject` with a summary 10 | of the thread's topic. 11 | 12 | ```json 13 | { 14 | "kind": 11, 15 | "content": "Good morning", 16 | "tags": [ 17 | ["subject", "GM"] 18 | ] 19 | } 20 | ``` 21 | 22 | Replies to `kind 11` MUST use [NIP-22](./22.md) `kind 1111` comments. Replies should 23 | always be to the root `kind 11` to avoid arbitrarily nested reply hierarchies. 24 | 25 | ```json 26 | { 27 | "kind": 1111, 28 | "content": "Cool beans", 29 | "tags": [ 30 | ["K", "11"], 31 | ["E", <event-id>, <relay-url>, <pubkey>] 32 | ] 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /84.md: -------------------------------------------------------------------------------- 1 | NIP-84 2 | ====== 3 | 4 | Highlights 5 | ---------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines `kind:9802`, a "highlight" event, to signal content a user finds valuable. 10 | 11 | ## Format 12 | The `.content` of these events is the highlighted portion of the text. 13 | 14 | `.content` might be empty for highlights of non-text based media (e.g. NIP-94 audio/video). 15 | 16 | ### References 17 | Events SHOULD tag the source of the highlight, whether nostr-native or not. 18 | `a` or `e` tags should be used for nostr events and `r` tags for URLs. 19 | 20 | When tagging a URL, clients generating these events SHOULD do a best effort of cleaning the URL from trackers 21 | or obvious non-useful information from the query string. 22 | 23 | ### Attribution 24 | Clients MAY include one or more `p` tags, tagging the original authors of the material being highlighted; this is particularly 25 | useful when highlighting non-nostr content for which the client might be able to get a nostr pubkey somehow 26 | (e.g. prompting the user or reading a `<meta name="nostr:nprofile1..." />` tag on the document). A role MAY be included as the 27 | last value of the tag. 28 | 29 | ```jsonc 30 | { 31 | "tags": [ 32 | ["p", "<pubkey-hex>", "<relay-url>", "author"], 33 | ["p", "<pubkey-hex>", "<relay-url>", "author"], 34 | ["p", "<pubkey-hex>", "<relay-url>", "editor"] 35 | ], 36 | // other fields... 37 | } 38 | ``` 39 | 40 | ### Context 41 | Clients MAY include a `context` tag, useful when the highlight is a subset of a paragraph and displaying the 42 | surrounding content might be beneficial to give context to the highlight. 43 | -------------------------------------------------------------------------------- /86.md: -------------------------------------------------------------------------------- 1 | NIP-86 2 | ====== 3 | 4 | Relay Management API 5 | -------------------- 6 | 7 | `draft` `optional` 8 | 9 | Relays may provide an API for performing management tasks. This is made available as a JSON-RPC-like request-response protocol over HTTP, on the same URI as the relay's websocket. 10 | 11 | When a relay receives an HTTP(s) request with a `Content-Type` header of `application/nostr+json+rpc` to a URI supporting WebSocket upgrades, it should parse the request as a JSON document with the following fields: 12 | 13 | ```json 14 | { 15 | "method": "<method-name>", 16 | "params": ["<array>", "<of>", "<parameters>"] 17 | } 18 | ``` 19 | 20 | Then it should return a response in the format 21 | 22 | ```json 23 | { 24 | "result": {"<arbitrary>": "<value>"}, 25 | "error": "<optional error message, if the call has errored>" 26 | } 27 | ``` 28 | 29 | This is the list of **methods** that may be supported: 30 | 31 | * `supportedmethods`: 32 | - params: `[]` 33 | - result: `["<method-name>", "<method-name>", ...]` (an array with the names of all the other supported methods) 34 | * `banpubkey`: 35 | - params: `["<32-byte-hex-public-key>", "<optional-reason>"]` 36 | - result: `true` (a boolean always set to `true`) 37 | * `listbannedpubkeys`: 38 | - params: `[]` 39 | - result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects 40 | * `allowpubkey`: 41 | - params: `["<32-byte-hex-public-key>", "<optional-reason>"]` 42 | - result: `true` (a boolean always set to `true`) 43 | * `listallowedpubkeys`: 44 | - params: `[]` 45 | - result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects 46 | * `listeventsneedingmoderation`: 47 | - params: `[]` 48 | - result: `[{"id": "<32-byte-hex>", "reason": "<optional-reason>"}]`, an array of objects 49 | * `allowevent`: 50 | - params: `["<32-byte-hex-event-id>", "<optional-reason>"]` 51 | - result: `true` (a boolean always set to `true`) 52 | * `banevent`: 53 | - params: `["<32-byte-hex-event-id>", "<optional-reason>"]` 54 | - result: `true` (a boolean always set to `true`) 55 | * `listbannedevents`: 56 | - params: `[]` 57 | - result: `[{"id": "<32-byte hex>", "reason": "<optional-reason>"}, ...]`, an array of objects 58 | * `changerelayname`: 59 | - params: `["<new-name>"]` 60 | - result: `true` (a boolean always set to `true`) 61 | * `changerelaydescription`: 62 | - params: `["<new-description>"]` 63 | - result: `true` (a boolean always set to `true`) 64 | * `changerelayicon`: 65 | - params: `["<new-icon-url>"]` 66 | - result: `true` (a boolean always set to `true`) 67 | * `allowkind`: 68 | - params: `[<kind-number>]` 69 | - result: `true` (a boolean always set to `true`) 70 | * `disallowkind`: 71 | - params: `[<kind-number>]` 72 | - result: `true` (a boolean always set to `true`) 73 | * `listallowedkinds`: 74 | - params: `[]` 75 | - result: `[<kind-number>, ...]`, an array of numbers 76 | * `blockip`: 77 | - params: `["<ip-address>", "<optional-reason>"]` 78 | - result: `true` (a boolean always set to `true`) 79 | * `unblockip`: 80 | - params: `["<ip-address>"]` 81 | - result: `true` (a boolean always set to `true`) 82 | * `listblockedips`: 83 | - params: `[]` 84 | - result: `[{"ip": "<ip-address>", "reason": "<optional-reason>"}, ...]`, an array of objects 85 | 86 | ### Authorization 87 | 88 | The request must contain an `Authorization` header with a valid [NIP-98](./98.md) event, except the `payload` tag is required. The `u` tag is the relay URL. 89 | 90 | If `Authorization` is not provided or is invalid, the endpoint should return a 401 response. 91 | -------------------------------------------------------------------------------- /89.md: -------------------------------------------------------------------------------- 1 | NIP-89 2 | ====== 3 | 4 | Recommended Application Handlers 5 | -------------------------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP describes `kind:31989` and `kind:31990`: a way to discover applications that can handle unknown event-kinds. 10 | 11 | ## Rationale 12 | 13 | Nostr's discoverability and transparent event interaction is one of its most interesting/novel mechanics. 14 | This NIP provides a simple way for clients to discover applications that handle events of a specific kind to ensure smooth cross-client and cross-kind interactions. 15 | 16 | ### Parties involved 17 | 18 | There are three actors to this workflow: 19 | 20 | * application that handles a specific event kind (note that an application doesn't necessarily need to be a distinct entity and it could just be the same pubkey as user A) 21 | * Publishes `kind:31990`, detailing how apps should redirect to it 22 | * user A, who recommends an app that handles a specific event kind 23 | * Publishes `kind:31989` 24 | * user B, who seeks a recommendation for an app that handles a specific event kind 25 | * Queries for `kind:31989` and, based on results, queries for `kind:31990` 26 | 27 | ## Events 28 | 29 | ### Recommendation event 30 | ```jsonc 31 | { 32 | "kind": 31989, 33 | "pubkey": <recommender-user-pubkey>, 34 | "tags": [ 35 | ["d", <supported-event-kind>], 36 | ["a", "31990:app1-pubkey:<d-identifier>", "wss://relay1", "ios"], 37 | ["a", "31990:app2-pubkey:<d-identifier>", "wss://relay2", "web"] 38 | ], 39 | // other fields... 40 | } 41 | ``` 42 | 43 | The `d` tag in `kind:31989` is the supported event kind this event is recommending. 44 | 45 | Multiple `a` tags can appear on the same `kind:31989`. 46 | 47 | The second value of the tag SHOULD be a relay hint. 48 | The third value of the tag SHOULD be the platform where this recommendation might apply. 49 | 50 | ## Handler information 51 | ```jsonc 52 | { 53 | "kind": 31990, 54 | "pubkey": "<application-pubkey>", 55 | "content": "<optional-kind:0-style-metadata>", 56 | "tags": [ 57 | ["d", <random-id>], 58 | ["k", <supported-event-kind>], 59 | ["web", "https://..../a/<bech32>", "nevent"], 60 | ["web", "https://..../p/<bech32>", "nprofile"], 61 | ["web", "https://..../e/<bech32>"], 62 | ["ios", ".../<bech32>"] 63 | ], 64 | // other fields... 65 | } 66 | ``` 67 | 68 | * `content` is an optional `metadata`-like stringified JSON object, as described in NIP-01. This content is useful when the pubkey creating the `kind:31990` is not an application. If `content` is empty, the `kind:0` of the pubkey should be used to display application information (e.g. name, picture, web, LUD16, etc.) 69 | * `k` tags' value is the event kind that is supported by this `kind:31990`. 70 | Using a `k` tag(s) (instead of having the kind of the `d` tag) provides: 71 | * Multiple `k` tags can exist in the same event if the application supports more than one event kind and their handler URLs are the same. 72 | * The same pubkey can have multiple events with different apps that handle the same event kind. 73 | * `bech32` in a URL MUST be replaced by clients with the NIP-19-encoded entity that should be loaded by the application. 74 | 75 | Multiple tags might be registered by the app, following NIP-19 nomenclature as the second value of the array. 76 | 77 | A tag without a second value in the array SHOULD be considered a generic handler for any NIP-19 entity that is not handled by a different tag. 78 | 79 | # Client tag 80 | When publishing events, clients MAY include a `client` tag. Identifying the client that published the note. This tag is a tuple of `name`, `address` identifying a handler event and, a relay `hint` for finding the handler event. This has privacy implications for users, so clients SHOULD allow users to opt-out of using this tag. 81 | 82 | ```jsonc 83 | { 84 | "kind": 1, 85 | "tags": [ 86 | ["client", "My Client", "31990:app1-pubkey:<d-identifier>", "wss://relay1"] 87 | ] 88 | // other fields... 89 | } 90 | ``` 91 | 92 | ## User flow 93 | A user A who uses a non-`kind:1`-centric nostr app could choose to announce/recommend a certain kind-handler application. 94 | 95 | When user B sees an unknown event kind, e.g. in a social-media centric nostr client, the client would allow user B to interact with the unknown-kind event (e.g. tapping on it). 96 | 97 | The client MIGHT query for the user's and the user's follows handler. 98 | 99 | ## Example 100 | 101 | ### User A recommends a `kind:31337`-handler 102 | User A might be a user of Zapstr, a `kind:31337`-centric client (tracks). Using Zapstr, user A publishes an event recommending Zapstr as a `kind:31337`-handler. 103 | 104 | ```jsonc 105 | { 106 | "kind": 31989, 107 | "tags": [ 108 | ["d", "31337"], 109 | ["a", "31990:1743058db7078661b94aaf4286429d97ee5257d14a86d6bfa54cb0482b876fb0:abcd", <relay-url>, "web"] 110 | ], 111 | // other fields... 112 | } 113 | ``` 114 | 115 | ### User B interacts with a `kind:31337`-handler 116 | User B might see in their timeline an event referring to a `kind:31337` event (e.g. a `kind:1` tagging a `kind:31337`). 117 | 118 | User B's client, not knowing how to handle a `kind:31337` might display the event using its `alt` tag (as described in NIP-31). When the user clicks on the event, the application queries for a handler for this `kind`: 119 | 120 | ``` 121 | ["REQ", <id>, { "kinds": [31989], "#d": ["31337"], "authors": [<user>, <users-contact-list>] }] 122 | ``` 123 | 124 | User B, who follows User A, sees that `kind:31989` event and fetches the `a`-tagged event for the app and handler information. 125 | 126 | User B's client sees the application's `kind:31990` which includes the information to redirect the user to the relevant URL with the desired entity replaced in the URL. 127 | 128 | ### Alternative query bypassing `kind:31989` 129 | Alternatively, users might choose to query directly for `kind:31990` for an event kind. Clients SHOULD be careful doing this and use spam-prevention mechanisms or querying high-quality restricted relays to avoid directing users to malicious handlers. 130 | 131 | ``` 132 | ["REQ", <id>, { "kinds": [31990], "#k": [<desired-event-kind>], "authors": [...] }] 133 | ``` 134 | -------------------------------------------------------------------------------- /92.md: -------------------------------------------------------------------------------- 1 | NIP-92 2 | ====== 3 | 4 | Media Attachments 5 | ----------------- 6 | 7 | Media attachments (images, videos, and other files) may be added to events by including a URL in the event content, along with a matching `imeta` tag. 8 | 9 | `imeta` ("inline metadata") tags add information about media URLs in the event's content. Each `imeta` tag SHOULD match a URL in the event content. Clients may replace imeta URLs with rich previews. 10 | 11 | The `imeta` tag is variadic, and each entry is a space-delimited key/value pair. 12 | Each `imeta` tag MUST have a `url`, and at least one other field. `imeta` may include 13 | any field specified by [NIP 94](./94.md). There SHOULD be only one `imeta` tag per URL. 14 | 15 | ## Example 16 | 17 | ```json 18 | { 19 | "content": "More image metadata tests don’t mind me https://nostr.build/i/my-image.jpg", 20 | "kind": 1, 21 | "tags": [ 22 | [ 23 | "imeta", 24 | "url https://nostr.build/i/my-image.jpg", 25 | "m image/jpeg", 26 | "blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$", 27 | "dim 3024x4032", 28 | "alt A scenic photo overlooking the coast of Costa Rica", 29 | "x <sha256 hash as specified in NIP 94>", 30 | "fallback https://nostrcheck.me/alt1.jpg", 31 | "fallback https://void.cat/alt1.jpg" 32 | ] 33 | ] 34 | } 35 | ``` 36 | 37 | ## Recommended client behavior 38 | 39 | When uploading files during a new post, clients MAY include this metadata 40 | after the file is uploaded and included in the post. 41 | 42 | When pasting URLs during post composition, the client MAY download the file 43 | and add this metadata before the post is sent. 44 | 45 | The client MAY ignore `imeta` tags that do not match the URL in the event content. 46 | -------------------------------------------------------------------------------- /94.md: -------------------------------------------------------------------------------- 1 | NIP-94 2 | ====== 3 | 4 | File Metadata 5 | ------------- 6 | 7 | `draft` `optional` 8 | 9 | The purpose of this NIP is to allow an organization and classification of shared files. So that relays can filter and organize in any way that is of interest. With that, multiple types of filesharing clients can be created. NIP-94 support is not expected to be implemented by "social" clients that deal with `kind:1` notes or by longform clients that deal with `kind:30023` articles. 10 | 11 | ## Event format 12 | 13 | This NIP specifies the use of the `1063` event type, having in `content` a description of the file content, and a list of tags described below: 14 | 15 | * `url` the url to download the file 16 | * `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. 17 | * `x` containing the SHA-256 hexencoded string of the file. 18 | * `ox` containing the SHA-256 hexencoded string of the original file, before any transformations done by the upload server 19 | * `size` (optional) size of file in bytes 20 | * `dim` (optional) size of file in pixels in the form `<width>x<height>` 21 | * `magnet` (optional) URI to magnet file 22 | * `i` (optional) torrent infohash 23 | * `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the file is being loaded by the client 24 | * `thumb` (optional) url of thumbnail with same aspect ratio 25 | * `image` (optional) url of preview image with same dimensions 26 | * `summary` (optional) text excerpt 27 | * `alt` (optional) description for accessibility 28 | * `fallback` (optional) zero or more fallback file sources in case `url` fails 29 | * `service` (optional) service type which is serving the file (eg. [NIP-96](96.md)) 30 | 31 | ```jsonc 32 | { 33 | "kind": 1063, 34 | "tags": [ 35 | ["url",<string with URI of file>], 36 | ["m", <MIME type>], 37 | ["x", <Hash SHA-256>], 38 | ["ox", <Hash SHA-256>], 39 | ["size", <size of file in bytes>], 40 | ["dim", <size of file in pixels>], 41 | ["magnet", <magnet URI> ], 42 | ["i", <torrent infohash>], 43 | ["blurhash", <value>], 44 | ["thumb", <string with thumbnail URI>, <Hash SHA-256>], 45 | ["image", <string with preview URI>, <Hash SHA-256>], 46 | ["summary", <excerpt>], 47 | ["alt", <description>] 48 | ], 49 | "content": "<caption>", 50 | // other fields... 51 | } 52 | ``` 53 | 54 | ## Suggested use cases 55 | 56 | * A relay for indexing shared files. For example, to promote torrents. 57 | * A pinterest-like client where people can share their portfolio and inspire others. 58 | * A simple way to distribute configurations and software updates. 59 | -------------------------------------------------------------------------------- /98.md: -------------------------------------------------------------------------------- 1 | NIP-98 2 | ====== 3 | 4 | HTTP Auth 5 | --------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines an ephemeral event used to authorize requests to HTTP servers using nostr events. 10 | 11 | This is useful for HTTP services which are built for Nostr and deal with Nostr user accounts. 12 | 13 | ## Nostr event 14 | 15 | A `kind 27235` (In reference to [RFC 7235](https://www.rfc-editor.org/rfc/rfc7235)) event is used. 16 | 17 | The `content` SHOULD be empty. 18 | 19 | The following tags MUST be included. 20 | 21 | * `u` - absolute URL 22 | * `method` - HTTP Request Method 23 | 24 | Example event: 25 | ```json 26 | { 27 | "id": "fe964e758903360f28d8424d092da8494ed207cba823110be3a57dfe4b578734", 28 | "pubkey": "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed", 29 | "content": "", 30 | "kind": 27235, 31 | "created_at": 1682327852, 32 | "tags": [ 33 | ["u", "https://api.snort.social/api/v1/n5sp/list"], 34 | ["method", "GET"] 35 | ], 36 | "sig": "5ed9d8ec958bc854f997bdc24ac337d005af372324747efe4a00e24f4c30437ff4dd8308684bed467d9d6be3e5a517bb43b1732cc7d33949a3aaf86705c22184" 37 | } 38 | ``` 39 | 40 | Servers MUST perform the following checks in order to validate the event: 41 | 1. The `kind` MUST be `27235`. 42 | 2. The `created_at` timestamp MUST be within a reasonable time window (suggestion 60 seconds). 43 | 3. The `u` tag MUST be exactly the same as the absolute request URL (including query parameters). 44 | 4. The `method` tag MUST be the same HTTP method used for the requested resource. 45 | 46 | When the request contains a body (as in POST/PUT/PATCH methods) clients SHOULD include a SHA256 hash of the request body in a `payload` tag as hex (`["payload", "<sha256-hex>"]`), servers MAY check this to validate that the requested payload is authorized. 47 | 48 | If one of the checks was to fail the server SHOULD respond with a 401 Unauthorized response code. 49 | 50 | Servers MAY perform additional implementation-specific validation checks. 51 | 52 | ## Request Flow 53 | 54 | Using the `Authorization` HTTP header, the `kind 27235` event MUST be `base64` encoded and use the Authorization scheme `Nostr` 55 | 56 | Example HTTP Authorization header: 57 | ``` 58 | Authorization: Nostr 59 | eyJpZCI6ImZlOTY0ZTc1ODkwMzM2MGYyOGQ4NDI0ZDA5MmRhODQ5NGVkMjA3Y2JhODIzMTEwYmUzYTU3ZGZlNGI1Nzg3MzQiLCJwdWJrZXkiOiI2M2ZlNjMxOGRjNTg1ODNjZmUxNjgxMGY4NmRkMDllMThiZmQ3NmFhYmMyNGEwMDgxY2UyODU2ZjMzMDUwNGVkIiwiY29udGVudCI6IiIsImtpbmQiOjI3MjM1LCJjcmVhdGVkX2F0IjoxNjgyMzI3ODUyLCJ0YWdzIjpbWyJ1IiwiaHR0cHM6Ly9hcGkuc25vcnQuc29jaWFsL2FwaS92MS9uNXNwL2xpc3QiXSxbIm1ldGhvZCIsIkdFVCJdXSwic2lnIjoiNWVkOWQ4ZWM5NThiYzg1NGY5OTdiZGMyNGFjMzM3ZDAwNWFmMzcyMzI0NzQ3ZWZlNGEwMGUyNGY0YzMwNDM3ZmY0ZGQ4MzA4Njg0YmVkNDY3ZDlkNmJlM2U1YTUxN2JiNDNiMTczMmNjN2QzMzk0OWEzYWFmODY3MDVjMjIxODQifQ 60 | ``` 61 | 62 | ## Reference Implementations 63 | - C# ASP.NET `AuthenticationHandler` [NostrAuth.cs](https://gist.github.com/v0l/74346ae530896115bfe2504c8cd018d3) 64 | -------------------------------------------------------------------------------- /99.md: -------------------------------------------------------------------------------- 1 | NIP-99 2 | ====== 3 | 4 | Classified Listings 5 | ------------------- 6 | 7 | `draft` `optional` 8 | 9 | This NIP defines `kind:30402`: an addressable event to describe classified listings that list any arbitrary product, service, or other thing for sale or offer and includes enough structured metadata to make them useful. 10 | 11 | The category of classifieds includes a very broad range of physical goods, services, work opportunities, rentals, free giveaways, personals, etc. and is distinct from the more strictly structured marketplaces defined in [NIP-15](15.md) that often sell many units of specific products through very specific channels. 12 | 13 | The structure of these events is very similar to [NIP-23](23.md) long-form content events. 14 | 15 | ### Draft / Inactive Listings 16 | 17 | `kind:30403` has the same structure as `kind:30402` and is used to save draft or inactive classified listings. 18 | 19 | ### Content 20 | 21 | The `.content` field should be a description of what is being offered and by whom. These events should be a string in Markdown syntax. 22 | 23 | ### Author 24 | 25 | The `.pubkey` field of these events are treated as the party creating the listing. 26 | 27 | ### Metadata 28 | 29 | - For "tags"/"hashtags" (i.e. categories or keywords of relevance for the listing) the `"t"` event tag should be used. 30 | - For images, whether included in the markdown content or not, clients SHOULD use `image` tags as described in [NIP-58](58.md). This allows clients to display images in carousel format more easily. 31 | 32 | The following tags, used for structured metadata, are standardized and SHOULD be included. Other tags may be added as necessary. 33 | 34 | - `"title"`, a title for the listing 35 | - `"summary"`, for short tagline or summary for the listing 36 | - `"published_at"`, for the timestamp (in unix seconds – converted to string) of the first time the listing was published. 37 | - `"location"`, for the location. 38 | - `"price"`, for the price of the thing being listed. This is an array in the format `[ "price", "<number>", "<currency>", "<frequency>" ]`. 39 | - `"price"` is the name of the tag 40 | - `"<number>"` is the amount in numeric format (but included in the tag as a string) 41 | - `"<currency>"` is the currency unit in 3-character ISO 4217 format or ISO 4217-like currency code (e.g. `"btc"`, `"eth"`). 42 | - `"<frequency>"` is optional and can be used to describe recurring payments. SHOULD be in noun format (hour, day, week, month, year, etc.) 43 | - - `"status"` (optional), the status of the listing. SHOULD be either "active" or "sold". 44 | 45 | #### `price` examples 46 | 47 | - $50 one-time payment `["price", "50", "USD"]` 48 | - €15 per month `["price", "15", "EUR", "month"]` 49 | - £50,000 per year `["price", "50000", "GBP", "year"]` 50 | 51 | Other standard tags that might be useful. 52 | 53 | - `"g"`, a geohash for more precise location 54 | 55 | ## Example Event 56 | 57 | ```jsonc 58 | { 59 | "kind": 30402, 60 | "created_at": 1675642635, 61 | // Markdown content 62 | "content": "Lorem [ipsum][nostr:nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9] dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nRead more at nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu.", 63 | "tags": [ 64 | ["d", "lorem-ipsum"], 65 | ["title", "Lorem Ipsum"], 66 | ["published_at", "1296962229"], 67 | ["t", "electronics"], 68 | ["image", "https://url.to.img", "256x256"], 69 | ["summary", "More lorem ipsum that is a little more than the title"], 70 | ["location", "NYC"], 71 | ["price", "100", "USD"], 72 | [ 73 | "e", 74 | "b3e392b11f5d4f28321cedd09303a748acfd0487aea5a7450b3481c60b6e4f87", 75 | "wss://relay.example.com" 76 | ], 77 | [ 78 | "a", 79 | "30023:a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919:ipsum", 80 | "wss://relay.nostr.org" 81 | ] 82 | ], 83 | "pubkey": "...", 84 | "id": "..." 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /BREAKING.md: -------------------------------------------------------------------------------- 1 | # Breaking Changes 2 | 3 | This is a history of NIP changes that potentially break pre-existing implementations, in 4 | reverse chronological order. 5 | 6 | | Date | Commit | NIP | Change | 7 | | ----------- | --------- | -------- | ------ | 8 | | 2024-11-12 | [2838e3bd](https://github.com/nostr-protocol/nips/commit/2838e3bd) | [29](29.md) | `kind: 12` and `kind: 10` were removed (use `kind: 1111` instead) | 9 | | 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | NIP-05 login was removed | 10 | | 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | `create_account` method was removed | 11 | | 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | `connect` params and result were changed | 12 | | 2024-10-29 | [f1e8d2c4](https://github.com/nostr-protocol/nips/commit/f1e8d2c4) | [46](46.md) | bunker URL should use `remote-signer-key` | 13 | | 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [71](71.md) | some tags were replaced with `imeta` tag | 14 | | 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [71](71.md) | `kind: 34237` was dropped | 15 | | 2024-10-07 | [7bb8997b](https://github.com/nostr-protocol/nips/commit/7bb8997b) | [55](55.md) | some fields and passing data were changed | 16 | | 2024-08-18 | [3aff37bd](https://github.com/nostr-protocol/nips/commit/3aff37bd) | [54](54.md) | content should be Asciidoc | 17 | | 2024-07-31 | [3ea2f1a4](https://github.com/nostr-protocol/nips/commit/3ea2f1a4) | [45](45.md) | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) was reverted | 18 | | 2024-07-30 | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) | [45](45.md) | NIP-45 was deprecated | 19 | | 2024-07-26 | [ecee40df](https://github.com/nostr-protocol/nips/commit/ecee40df) | [19](19.md) | `nrelay` was deprecated | 20 | | 2024-07-23 | [0227a2cd](https://github.com/nostr-protocol/nips/commit/0227a2cd) | [01](01.md) | events should be sorted by id after created_at | 21 | | 2024-06-06 | [58e94b20](https://github.com/nostr-protocol/nips/commit/58e94b20) | [25](25.md) | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) was reverted | 22 | | 2024-06-06 | [a6dfc7b5](https://github.com/nostr-protocol/nips/commit/a6dfc7b5) | [55](55.md) | NIP number was changed | 23 | | 2024-05-25 | [5d1d1c17](https://github.com/nostr-protocol/nips/commit/5d1d1c17) | [71](71.md) | 'aes-256-gcm' tag was removed | 24 | | 2024-05-07 | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) | [25](25.md) | e-tags were changed to not include entire thread | 25 | | 2024-04-30 | [bad88262](https://github.com/nostr-protocol/nips/commit/bad88262) | [34](34.md) | 'earliest-unique-commit' tag was removed (use 'r' tag instead) | 26 | | 2024-02-25 | [4a171cb0](https://github.com/nostr-protocol/nips/commit/4a171cb0) | [18](18.md) | quote repost should use `q` tag | 27 | | 2024-02-21 | [c6cd655c](https://github.com/nostr-protocol/nips/commit/c6cd655c) | [46](46.md) | Params were stringified | 28 | | 2024-02-16 | [cbec02ab](https://github.com/nostr-protocol/nips/commit/cbec02ab) | [49](49.md) | Password first normalized to NFKC | 29 | | 2024-02-15 | [afbb8dd0](https://github.com/nostr-protocol/nips/commit/afbb8dd0) | [39](39.md) | PGP identity was removed | 30 | | 2024-02-07 | [d3dad114](https://github.com/nostr-protocol/nips/commit/d3dad114) | [46](46.md) | Connection token format was changed | 31 | | 2024-01-30 | [1a2b21b6](https://github.com/nostr-protocol/nips/commit/1a2b21b6) | [59](59.md) | 'p' tag became optional | 32 | | 2023-01-27 | [c2f34817](https://github.com/nostr-protocol/nips/commit/c2f34817) | [47](47.md) | optional expiration tag should be honored | 33 | | 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [02](02.md) | list entries should be chronological | 34 | | 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [51](51.md) | list entries should be chronological | 35 | | 2023-12-30 | [29869821](https://github.com/nostr-protocol/nips/commit/29869821) | [52](52.md) | 'name' tag was removed (use 'title' tag instead) | 36 | | 2023-12-27 | [17c67ef5](https://github.com/nostr-protocol/nips/commit/17c67ef5) | [94](94.md) | 'aes-256-gcm' tag was removed | 37 | | 2023-12-03 | [0ba45895](https://github.com/nostr-protocol/nips/commit/0ba45895) | [01](01.md) | WebSocket status code `4000` was replaced by 'CLOSED' message | 38 | | 2023-11-28 | [6de35f9e](https://github.com/nostr-protocol/nips/commit/6de35f9e) | [89](89.md) | 'client' tag value was changed | 39 | | 2023-11-20 | [7822a8b1](https://github.com/nostr-protocol/nips/commit/7822a8b1) | [51](51.md) | `kind: 30000` and `kind: 30001` were deprecated | 40 | | 2023-11-11 | [cbdca1e9](https://github.com/nostr-protocol/nips/commit/cbdca1e9) | [84](84.md) | 'range' tag was removed | 41 | | 2023-11-10 | [c945d8bd](https://github.com/nostr-protocol/nips/commit/c945d8bd) | [32](32.md) | 'l' tag annotations was removed | 42 | | 2023-11-07 | [108b7f16](https://github.com/nostr-protocol/nips/commit/108b7f16) | [01](01.md) | 'OK' message must have 4 items | 43 | | 2023-10-17 | [cf672b76](https://github.com/nostr-protocol/nips/commit/cf672b76) | [03](03.md) | 'block' tag was removed | 44 | | 2023-09-29 | [7dc6385f](https://github.com/nostr-protocol/nips/commit/7dc6385f) | [57](57.md) | optional 'a' tag was included in `zap receipt` | 45 | | 2023-08-21 | [89915e02](https://github.com/nostr-protocol/nips/commit/89915e02) | [11](11.md) | 'min_prefix' was removed | 46 | | 2023-08-20 | [37c4375e](https://github.com/nostr-protocol/nips/commit/37c4375e) | [01](01.md) | replaceable events with same timestamp should be retained event with lowest id | 47 | | 2023-08-15 | [88ee873c](https://github.com/nostr-protocol/nips/commit/88ee873c) | [15](15.md) | 'countries' tag was renamed to 'regions' | 48 | | 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [12](12.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 | 49 | | 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [16](16.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 | 50 | | 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [20](20.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 | 51 | | 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [33](33.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 | 52 | | 2023-08-11 | [d87f8617](https://github.com/nostr-protocol/nips/commit/d87f8617) | [25](25.md) | empty `content` should be considered as "+" | 53 | | 2023-08-01 | [5d63b157](https://github.com/nostr-protocol/nips/commit/5d63b157) | [57](57.md) | 'zap' tag was changed | 54 | | 2023-07-15 | [d1814405](https://github.com/nostr-protocol/nips/commit/d1814405) | [01](01.md) | `since` and `until` filters should be `since <= created_at <= until` | 55 | | 2023-07-12 | [a1cd2bd8](https://github.com/nostr-protocol/nips/commit/a1cd2bd8) | [25](25.md) | custom emoji was supported | 56 | | 2023-06-18 | [83cbd3e1](https://github.com/nostr-protocol/nips/commit/83cbd3e1) | [11](11.md) | 'image' was renamed to 'icon' | 57 | | 2023-04-13 | [bf0a0da6](https://github.com/nostr-protocol/nips/commit/bf0a0da6) | [15](15.md) | different NIP was re-added as NIP-15 | 58 | | 2023-04-09 | [fb5b7c73](https://github.com/nostr-protocol/nips/commit/fb5b7c73) | [15](15.md) | NIP-15 was merged into NIP-01 | 59 | | 2023-03-29 | [599e1313](https://github.com/nostr-protocol/nips/commit/599e1313) | [18](18.md) | NIP-18 was bring back | 60 | | 2023-03-15 | [e1004d3d](https://github.com/nostr-protocol/nips/commit/e1004d3d) | [19](19.md) | `1: relay` was changed to optionally | 61 | 62 | Breaking changes prior to 2023-03-01 are not yet documented. 63 | 64 | ## NOTES 65 | 66 | - If it isn't clear that a change is breaking or not, we list it. 67 | - The date is the date it was merged, not necessarily the date of the commit. 68 | -------------------------------------------------------------------------------- /C7.md: -------------------------------------------------------------------------------- 1 | NIP-C7 2 | ====== 3 | 4 | Chats 5 | ----- 6 | 7 | `draft` `optional` 8 | 9 | A chat message is a `kind 9` event. 10 | 11 | ```json 12 | { 13 | "kind": 9, 14 | "content": "GM", 15 | "tags": [] 16 | } 17 | ``` 18 | 19 | A reply to a `kind 9` is an additional `kind 9` which quotes the parent using a `q` tag. 20 | 21 | ```json 22 | { 23 | "kind": 9, 24 | "content": "nostr:nevent1...\nyes", 25 | "tags": [ 26 | ["q", <event-id>, <relay-url>, <pubkey>] 27 | ] 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # NIPs日本語訳への貢献 2 | 3 | NIPs日本語訳への貢献に興味をお持ちいただきありがとうございます。これは、[NIPs (Nostr Implementation Possibilities)](https://github.com/nostr-protocol/nips) を日本語訳するプロジェクトへ貢献するためのガイドラインです。このテキストは常に改善の余地を含んでおり、Pull Requestによる提案を歓迎します。 4 | 5 | ## このプロジェクトの目的 6 | 7 | このプロジェクトでは、[Nostr](https://github.com/nostr-protocol/nostr)の実装可能性について記述されたNIPsの日本語の翻訳作業を行います。NIPsの最新の記述に正しく追従した日本語訳を行うことで日本語話者の方々の理解を促進し、Nostrの日本における普及に貢献することを目的としています。 8 | 9 | ## 貢献の仕方 10 | 11 | ### 翻訳作業 12 | 13 | 1. まず作業の重複を防ぐために、mainブランチにおいて未翻訳かつ[Progress checklist](https://github.com/nostr-jp/nips-ja/issues/1)のIssueにて誰も担当していないNIP番号を選択してください。 14 | 2. そのNIPを自分が担当することを[Progress checklist](https://github.com/nostr-jp/nips-ja/issues/1)で宣言してください。 15 | 3. mainブランチをフォークして1週間以内に翻訳を完了し、Pull Requestを作成してください。 16 | 4. Pull Requestがtextlintというスタイルチェッカーによる自動チェックを通過し、メンテナによるレビューが完了するとmainブランチにマージされて翻訳完了です。 17 | 18 | #### 翻訳ガイド 19 | 20 | - Pull Requestを作成する前に、ローカルリポジトリで`npm install`を実行してtextlintをインストールし、`npm run lint`を実行してtextlintによる自動チェックを通過することを確認してください。 21 | - 自動チェックされる内容は現在詳細を検討中ですが、[About style guide](https://github.com/nostr-jp/nips-ja/issues/6)のIssueにて暫定案を示しています。 22 | - gitで翻訳を管理する都合上、**対応する訳文は原文の行位置と厳密に対応させる**必要があります。これを守らないと、原文の変更が正しく追従できなくなります。 23 | - 原文に登場する"MUST"、"SHOULD"、"MAY"は、[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119)の規約を借用しています。これらはそれぞれ「しなければならない」「するべきである」「してもかまわない」と訳し、その後に括弧書きで元の単語を付記してください。 24 | - 例: `...連絡先リストを削除するべきである␣(SHOULD)␣。` 25 | - 翻訳によってNIPのタイトルが新しく訳される、または訳が更新される場合、[README.md](./README.md)中のNIPリストの対応する項目も同時に変更してください。 26 | - 強調などの表現は、原文の表現に合わせてください。 27 | - コードブロックの中は以下に従って翻訳してください。 28 | - コメントと文字列を翻訳してください。 29 | - 変数名と関数名、jsonのキー部分は翻訳しないでください。 30 | - コードが機能する状態を保ってください。 31 | - 括弧の両端は半角スペースを設けてください。 32 | 33 | #### 参考 34 | 35 | Nostrのscrapboxには、NIPsの仮翻訳やNostrについての関連情報が数多くまとめられています。ただし、情報が古い場合もあるので必ず原文をチェックしながら作業してください。 36 | 37 | [Nostr scrapbox](https://scrapbox.io/nostr/NIP) 38 | 39 | ### 翻訳・スタイルガイドの問題点の報告 40 | 41 | このプロジェクトの翻訳・スタイルガイドに問題点があると感じた場合は、Issueを作成したり、既存のIssueに書き込む形で報告してください。また、そのIssueに対する修正案をPull Requestとして作成していただけるとより助かります。 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "textlint .", 4 | "lint-staged": "lint-staged", 5 | "prepare": "husky" 6 | }, 7 | "devDependencies": { 8 | "husky": "^9.1.7", 9 | "lint-staged": "^15.5.1", 10 | "textlint": "^14.6.0", 11 | "textlint-rule-preset-ja-spacing": "^2.4.3", 12 | "textlint-rule-preset-ja-technical-writing": "^12.0.2" 13 | }, 14 | "lint-staged": { 15 | "*.md": "textlint" 16 | } 17 | } 18 | --------------------------------------------------------------------------------