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 中相同的 EVENTREQCLOSE 消息。

过滤器

过滤器指定要检索的事件,包含的字段有 idsauthorskinds#e/#p/#tsinceuntillimit。单个过滤器内的条件使用 AND 逻辑。单个 REQ 中的多个过滤器使用 OR 逻辑。

互操作说明

两个细节导致了许多实现中的 bug。第一,客户端应将中继器响应视为最终一致的,而非全局有序的,因为不同的中继器可能返回不同的历史子集。第二,可替换和可寻址事件意味着"最新"是协议模型的一部分,因此当多个中继器不一致时,客户端需要确定性的规则来选择最新的有效事件。


主要来源:

提及于:

另请参阅: