📖

Nostrについて学んで整理してみた 用語まとめ

2024/11/22に公開

はじめに

現在、弊社の社内開発でNostrを利用しています。
https://zenn.dev/astrskcojp/articles/355fe1162962c2

備忘録を兼ねてNostrについてまとめてみようと思います。

内容が多かったので、3つの記事に分けてまとめました。

  1. 用語解説(今回)
  2. リレーサーバーを立てる
  3. 簡易SNSを作る

1は開発をせずにNostrの知識周りだけをまとめています。
2と3は実践編です。

今回は1です。

ゼロベースから開発するのではなく、 nostr-toolsNos2xを利用しています。

独学なので間違えているところが多々あるかと思いますが、ご了承ください。(もし間違いがあればコメントで指摘してもらえると嬉しいです🙏)

対象読者

  • Nostrに興味がある人
  • Nostrを使ってWebアプリを実装したい人

Nostrとは

Nostr(Notes and Other Stuff Transmitted by Relays)は、分散型のソーシャルネットワークプロトコルです。分散型のアプリを作るために考案されました。

「リレー」と呼ばれるサーバーに接続して情報を送受信できます。リレーは誰でも立ち上げることができ、ユーザーも好きなリレーを選択できます。
これにより、ネットワーク全体が分散化され、特定の組織や個人に制御されるリスクを減らせます。

下記の記事がとてもわかりやすく解説されていました。
https://zenn.dev/kaiji/articles/e855dccba73211

Nostrに関する用語

  • クライアント
  • リレー
  • イベント
  • 公開鍵
  • 秘密鍵
  • 署名
  • NIP

クライアント

Nostrに則って、データを送信・受信するアプリケーションやソフトウェア。

これらが有名なクライアントです。

クライアントの役目は、データを送信・受信することです。そのため、繋げるリレー(サーバー)が同じであれば、どのクライアントを使っても同じ内容の投稿を閲覧できます。(もちろんクライアントのUIなどは異なります)

リレー

クライアントが送信したデータ(イベント)を受信して保存します。データベースのようなものです。

リレーは誰でも運営でき、他のリレーに依存することもありません。

イベント

メッセージやユーザーアクションの単位。

投稿した内容やプロフィール、投稿へのリアクションなどあらゆるものを「イベント」として扱います。各イベントは同じデータ構造をしていて、kindという数値で区別されます。

公開鍵

ユーザーを識別するための鍵。

他のユーザーやリレーに共有され、公開鍵によりイベント作成者を判断します。
SNSなどにおけるIDです。

秘密鍵

作成したイベントを自分のものだと証明するための鍵。

秘密鍵はユーザーの手元にのみ保管され、イベントの真正性を保証するために使用されます。
SNSなどにおけるPasswordです。

署名

イベントに対して秘密鍵で行う暗号学的な署名。

署名を行うことで、特定のユーザーによってイベントが作成されたことを証明します。

NIP

Nostrプロトコルに関する提案や拡張機能を示す文書。NIPは、新しい機能やプロトコルの改善点についての提案をまとめたもので、NIP-01のように番号が付けられています。

こちらにまとめられています。(日本語訳もありますが、最新版ではない場合もあるので注意)

イベント

SNSでよく使うイベントをまとめてみました。

基本構造

{
  id: "event_id",               // イベントID(ハッシュ値で一意となる)
  pubkey: "public_key",         // イベントの送信者の公開鍵
  created_at: 1234567890,       // タイムスタンプ(UTCエポック秒)
  kind: 1,                      // イベントの種類
  tags: [                       // 関連するタグやメタデータ
    ["e", "event_id", "relay_url", "投稿イベント"],
    ["p", "pubkey", "relay_url"]
  ],
  content: "メッセージ",          // イベントの本文(メッセージやデータ)
  sig: "signature"              // 秘密鍵で署名された署名(イベント全体に対するもの)
}

こちらが基本的な構造です。

  • id
  • pubkey
  • created_at
  • sig

この4つはどのイベントでも共通している内容です。

  • kind
  • tags
  • content

この3つはイベントの種類(kind)によって内容が異なります。

  • 0: プロフィール
  • 1: 投稿・メンション・リプライ
  • 3: フォロー・アンフォロー
  • 6: リポスト
  • 7: リアクション

今回は、この5つの具体例を記載します。
わかりやすいようid, pubkey, created_at, sigのキーは非表示にしています。

プロフィール

kind: 0は、いわゆる「プロフィール」です。

{
  kind: 0,
  tags: [],
  content: "{
    \"name\": \"名前\",
    \"about\": \"Just a Nostr user.\",
    \"picture\": \"https://example.com/profile.jpg\",
    \"nip05\": \"john@example.com\"
  }"
}
  • kind: 0を指定します
  • tags: 基本的に[]を指定します
  • content: JSON形式でプロフィール内容を指定します
{
  name: "名前", // ユーザー名
  about: "自己紹介です。", // 自己紹介
  picture: "https://example.com/profile.jpg\" // アイコン画像のURL
}

contentの内容はこんな感じです。

投稿

{
  kind: 1,
  tags: [],
  content:"投稿イベントの作成",
}
  • kind: 1を指定します
  • tags: 基本的に[]を指定します
  • content: 投稿したい内容を指定します

メンション

メンションは投稿と同じkindです。tagsでメンション先を指定します。

{
  kind: 1,
  tags: [
    ["p", "返信先の公開鍵"]
 ],
  content: "リプライ"
}
  • kind: 1を指定します
  • tags:メンション先を指定します。["p", "返信先の公開鍵"]
  • content: 投稿したい内容を指定します

リプライ

リプライは投稿と同じkindです。tagsでリプライ先を指定します。

{
  kind: 1,
  tags: [
    ["e", "リプライ先の投稿id", "", "root"]
  ],
  content: "リプライ",
}
  • kind: 1を指定します
  • tags: リプライ先を指定します。["e", "リプライ先の投稿id", "リレーサーバーURL", "root"]
  • content: 投稿したい内容を指定します

["e", "リプライ先の投稿id", "リレーサーバーURL", "root"]"root"は、スレッド元を指します。

リプライが連なっている場合は、以下のように"root""reply"に変えます

[
  ["e", "スレッド内の投稿id1", "リレーサーバーURL", "reply"],
  ["e", "スレッド内の投稿id2", "リレーサーバーURL", "reply"],
  ["e", "リプライ先の投稿id", "リレーサーバーURL", "root"]
]

Xのようにリプライがメンションを兼ねている場合は、以下のようにtagsにメンション先を追加する必要があります。

{
  kind: 1,
  tags: [
    ["p", "返信先の公開鍵"],
    ["e", "リプライ先の投稿id", "リレーサーバーURL", "root"]
  ],
  content: "リプライ",
}

フォロー・アンフォロー

フォローとアンフォローは区別されず、フォローリストをサーバーに送る形で行います。

{
  kind: 3,
  tags: [
    ["p", "フォロー対象の公開鍵", "リレーサーバーURL", "フォロー対象の愛称"]
  ],
  content: "",
}
  • kind: 3を指定します
  • tags: フォローリストを指定します
  • content: ""を指定します

フォローリストは二重配列になっていて

["p", "フォロー対象の公開鍵", "リレーサーバーURL", "フォロー対象の愛称"]

という形式の配列で、フォローしている人の情報をまとめます。

リレーサーバーURLは、空でも大丈夫です。
フォロー対象の愛称は、よくわかっていないのですがフォロー対象のプロフィールの名前にすれば問題ないかと思います。

リポスト

{
  kind: 6,
  tags: [
  ["e", "リポスト対象のイベントID", "リポスト対象のイベントが取得できるリレーサーバーのURL"]
 ],
  content: JSON.stringify(repostTargetEvent),
}
  • kind: 6を指定します
  • tags: リポスト先を指定します。["e", "リポスト対象のイベントID", "リレーサーバーURL"]
  • content: リポスト対象のイベントを指定します(文字列化が必要)

リアクション

  kind: 7,
  tags: [
    ["e", "リアクション対象のイベントID", "リレーサーバーURL"],
    ["p", ""リアクション対象のイベントID"]
  ],
  content: "+",
  • kind: 7を指定します
  • tags: リアクション先を指定します。["e", "リアクション対象のイベントID", "リレーサーバーURL"]
  • content: リアクションの文字列を指定します。

リアクションは絵文字か+-が指定できます。

  • +: 肯定的な意味。『いいね』
  • -: 否定的な意味。『わるいね』

まとめ

今回は、nostrの基本的なところをまとめました。次回はリレーサーバーを建ててみたいと思います。

https://zenn.dev/astrskcojp/articles/9e80e72564e767

ASTRSK

Discussion