NIP-01:基础协议
NIP-01 定义了 Nostr 其余部分所依赖的基础事件模型和中继器协议。任何客户端、中继器或库要使用 Nostr,都从这里开始。
工作原理
事件是 Nostr 中唯一的对象类型。用户资料、笔记、反应、中继器列表以及许多应用特定的数据载荷都使用相同的七字段信封:
- id:序列化事件的 SHA256 哈希(唯一标识符)
- pubkey:创建者的公钥(32 字节 hex,secp256k1)
- created_at:Unix 时间戳
- kind:对事件类型进行分类的整数
- tags:用于元数据的数组的数组
- content:载荷(解释方式取决于 kind)
- sig:证明真实性的 Schnorr 签名
事件 id 是序列化事件数据的 SHA256 哈希,而非任意标识符。这在实践中很重要:更改任何字段(包括标签顺序或时间戳)都会产生不同的事件,并需要新的签名。
Kind 分类
Kind 决定了中继器如何存储和处理事件:
- 常规事件(1、2、4-44、1000-9999):正常存储,保留所有版本
- 可替换事件(0、3、10000-19999):每个 pubkey 仅保留最新版本
- 临时事件(20000-29999):不存储,仅转发给订阅者
- 可寻址事件(30000-39999):每个 pubkey + kind +
d标签组合保留最新版本
核心 kind 包括:0(用户元数据)、1(文本笔记)和 3(关注列表)。
客户端与中继器通信
客户端通过 WebSocket 连接使用 JSON 数组与中继器通信:
客户端到中继器:
["EVENT", <event>]- 发布事件["REQ", <sub-id>, <filter>, ...]- 订阅事件["CLOSE", <sub-id>]- 结束订阅
中继器到客户端:
["EVENT", <sub-id>, <event>]- 传递匹配的事件["EOSE", <sub-id>]- 已存储事件结束(现在进入实时流模式)["OK", <event-id>, <true|false>, <message>]- 接受/拒绝确认["NOTICE", <message>]- 人类可读的消息
在实践中,大多数高级 NIP 不会更改传输层。它们定义新的事件 kind、标签或解释规则,同时仍使用 NIP-01 中相同的 EVENT、REQ 和 CLOSE 消息。
过滤器
过滤器指定要检索的事件,包含的字段有 ids、authors、kinds、#e/#p/#t、since、until 和 limit。单个过滤器内的条件使用 AND 逻辑。单个 REQ 中的多个过滤器使用 OR 逻辑。
互操作说明
两个细节导致了许多实现中的 bug。第一,客户端应将中继器响应视为最终一致的,而非全局有序的,因为不同的中继器可能返回不同的历史子集。第二,可替换和可寻址事件意味着"最新"是协议模型的一部分,因此当多个中继器不一致时,客户端需要确定性的规则来选择最新的有效事件。
主要来源:
提及于:
另请参阅: