進階
NIP-01 基本協議
深入了解 Nostr 的基本協議規範,事件格式、簽名驗證和客戶端-中繼器通訊。
18 分鐘
NIP-01 概述
NIP-01 定義了 Nostr 協議的基本元素:事件格式、簽名方案、 客戶端與中繼器之間的通訊協議,以及基本的訊息類型。 這是所有 Nostr 應用的基礎。
核心原則: Nostr 的設計極度簡單——每個用戶只有一對密鑰,所有內容都是簽名的事件。 中繼器只是轉發事件,不需要信任它們。
事件格式
每個事件都是一個 JSON 物件,包含以下欄位:
{
"id": "事件 ID(內容的 SHA256 雜湊)",
"pubkey": "發布者的公鑰(32 bytes hex)",
"created_at": 1234567890, // Unix 時間戳
"kind": 1, // 事件類型
"tags": [ // 標籤陣列
["e", "引用的事件 ID"],
["p", "提及的公鑰"],
["t", "hashtag"]
],
"content": "事件內容",
"sig": "Schnorr 簽名(64 bytes hex)"
} 事件 ID 計算
事件 ID 是以下 JSON 序列化的 SHA256 雜湊:
// 序列化格式
[
0, // 固定值
<pubkey>, // 發布者公鑰
<created_at>, // 時間戳
<kind>, // 事件類型
<tags>, // 標籤陣列
<content> // 內容字串
]
// 計算
id = SHA256(JSON.stringify(serialized)) 常見事件類型 (Kind)
| Kind | 名稱 | 說明 |
|---|---|---|
| 0 | Metadata | 用戶個人資料(名稱、頭像、簡介) |
| 1 | Short Text Note | 純文字貼文 |
| 2 | Recommend Relay | 推薦中繼器(已棄用) |
| 3 | Contacts | 關注列表 |
| 4 | Encrypted DM | 加密私訊(NIP-04) |
| 5 | Event Deletion | 刪除請求 |
| 7 | Reaction | 按讚、表情反應 |
客戶端-中繼器通訊
Nostr 使用 WebSocket 進行通訊,有三種訊息類型:
CLIENT → RELAY
EVENT發布事件REQ訂閱查詢CLOSE關閉訂閱
RELAY → CLIENT
EVENT返回事件OK確認收到EOSE訂閱結束NOTICE通知訊息
訊息格式
所有訊息都是 JSON 陣列,第一個元素是訊息類型
訂閱過濾器
REQ 訊息使用過濾器來查詢事件:
// 訂閱格式
["REQ", "<subscription_id>", <filter1>, <filter2>, ...]
// 過濾器選項
{
"ids": ["<event_id>", ...], // 特定事件 ID
"authors": ["<pubkey>", ...], // 特定作者
"kinds": [0, 1, 3], // 事件類型
"#e": ["<event_id>", ...], // 引用事件
"#p": ["<pubkey>", ...], // 提及用戶
"#t": ["bitcoin", "nostr"], // 標籤
"since": 1234567890, // 時間範圍起始
"until": 1234567899, // 時間範圍結束
"limit": 100 // 數量限制
} 實際範例
發布貼文
// 客戶端發送
["EVENT", {
"id": "abc123...",
"pubkey": "npub1...",
"created_at": 1704067200,
"kind": 1,
"tags": [],
"content": "Hello, Nostr!",
"sig": "sig123..."
}]
// 中繼器回應
["OK", "abc123...", true, ""] 訂閱用戶貼文
// 客戶端請求
["REQ", "my-sub", {
"authors": ["npub1..."],
"kinds": [1],
"limit": 50
}]
// 中繼器回應(多個事件)
["EVENT", "my-sub", {...event1...}]
["EVENT", "my-sub", {...event2...}]
["EOSE", "my-sub"] // 歷史事件結束
// 新事件即時推送
["EVENT", "my-sub", {...new_event...}] 標籤系統
標籤是 Nostr 事件中的重要元素,用於關聯和索引:
| 標籤 | 格式 | 用途 |
|---|---|---|
| e | ["e", "<event_id>", "<relay>"] | 引用/回覆事件 |
| p | ["p", "<pubkey>"] | 提及用戶 |
| t | ["t", "hashtag"] | 主題標籤 |
| a | ["a", "<kind>:<pubkey>:<d>"] | 引用可替換事件 |
簽名驗證
Nostr 使用 Schnorr 簽名(secp256k1 曲線):
// 驗證步驟
1. 重新計算事件 ID
computed_id = SHA256(serialize(event))
2. 驗證 ID 匹配
assert(event.id === computed_id)
3. 驗證 Schnorr 簽名
assert(schnorr.verify(event.sig, event.id, event.pubkey)) 安全提示: 客戶端必須驗證每個收到的事件的簽名,不能信任中繼器。 這是 Nostr 去中心化信任模型的核心。
已複製連結