-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnostr_doc.txt
472 lines (368 loc) · 15.4 KB
/
nostr_doc.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
nostr协议细节
相关链接
官网 https://nostr.com/
协议简介和背景 https://github.com/nostr-protocol/nostr
协议规范 https://github.com/nostr-protocol/nips
相关产品收集 https://www.nostr.net/
中继列表 https://nostr.watch/relays/find
日本开发者做的趋势 https://nostrends.vercel.app/
排行榜和相关工具 https://nostr.band/
iris网页版客户端 https://iris.to/
snort网页版客户端 https://snort.social/
Damus iOS客户端 https://damus.io/
hamstr 仿twitter网页版客户端 https://hamstr.to/home
daisy rn写的客户端 https://github.com/neb-b/daisy
amethyst 安卓客户端 https://github.com/vitorpamplona/amethyst
alby 浏览器插件 https://getalby.com/
nos2x 浏览器插件 https://github.com/fiatjaf/nos2x
协议内容简介
nostr是一个协议,不是一个实际的产品,所有实现了这个协议的客户端和服务器都能实现互相交互
主要目的是实现抗审查的全球社交网络
特点
不依赖中心服务器 -> 中心化社交或者孤岛社交
非对称加密和签名 -> 防篡改
不依赖p2p 不是区块链
简单总结(机翻自官网)
每个人都运行一个客户端。 它可以是本机客户端、Web 客户端等。要发布某些内容,您可以写一篇文章,用您的密钥对其签名并将其发送到多个中继(由其他人或您自己托管的服务器)。 要从其他人那里获得更新,您可以询问多个中继他们是否了解这些其他人。 任何人都可以运行中继。 继电器非常简单和愚蠢。 除了接受某些人的帖子并转发给其他人之外,它什么都不做。 不必信任中继。 签名在客户端进行验证。
术语名次解释
客户端
即常见的web端、app等,也有为了实现某些功能的服务端
中继
即传统的服务器端,是远程实现数据持久化的服务,客户端通过向中继发送事件
或发送订阅以取得数据
用户id
nostr使用非对称加密标识用户,通过数字签名认证消息,公钥就是用户的id
事件
事件是nostr协议里唯一的数据实体,所以客户端和中继交互的数据都是一个事件
事件有不同的类型和不同的处理方式,具体定义在nip-01和nip-16
01
定义了协议的主要交互方式和事件格式,以及几种基本的事件类型
客户端和中继之间通过websocket交互,交互的数据为json,基本格式如下:
客户端 -> 中继
请求订阅
["REQ", "id", {}]
关闭订阅
["CLOSE", "id"]
发送事件
["EVENT", {}]
中继 -> 客户端
事件
["EVENT", "id", {}]
订阅内容结束
["EOSE", "id"]
提醒消息
["NOTICE", "message"]
所有的数据是一个数组,第一个参数为请求类型,固定为字符串
其他参数视不同类型的请求有所不同
REQ为请求订阅,第二个参数为订阅id,由客户端生成,
可随机也可固定,相同的id中继会覆盖并重新响应这个请求,
第三个往后的所有数据都是一个对象,包含订阅的过滤条件,
当含有多个过滤条件时,这些条件的判断方式为“或”
CLOSE为关闭请求订阅,当一个请求订阅被中继响应时,
中继会根据过滤条件查询已保存的数据并返回给客户端,并且只查询一次,
发送完成后会返回客户端EOSE的消息,但这个订阅仍然会持续保留,
并且有了新的事件会继续通过这个订阅返回给客户端,
客户端可以发送这个类型的消息告诉中继关闭这个订阅,
第二个参数为订阅的id
EVENT为发送事件,第二个参数为事件对象
事件的格式固定如下:
{
"id": "123123123", // 事件id,32字节的hex字符串
"kind": 1, // 事件类型,数字
"content": "", // 事件内容,字符串
"tags": [], // tags,二维数组字符串
"pubkey": "123", // 用户公钥,32字节的hex字符串
"created_at", 123, // 秒级时间戳
"sig": "123" // 事件签名,64字节的hex字符串
}
事件id通过以下方式生成:
将以下数据生成json字符串,不包含换行和空白符:
[0,pubkey,created_at,kind,tags,content]
然后对这个字符串计算SHA-256哈希值,转成hex字符串
事件签名为对事件id(二进制)使用私钥签名
如此下来,整个事件数据唯一且可认证
订阅请求的筛选器格式如下:
{
"ids": [""], // 事件id列表,hex字符串数组
"authors": [""], // 用户id列表,hex字符串数组
"kinds": [1], // 事件类型列表,数字数组
"#e": [""], // tags里的事件id列表,hex字符串数组
"#p": [""], // tags里的用户ID列表,hex字符串数组
"since": 123, // created_at的起始事件,数字
"until": 123, // created_at的结束事件,数字
"limit": 123 // 数量,这个实际由中继决定,取limit和中继设定的最小值
}
所有参数都是可选的,最好带上固定的参数避免数据流太大获取到太多不需要的数据
并且有些中继会拒绝没有参数的请求
中继返回的消息类型如下:
EVENT事件类型,第二个参数为订阅id,第三个参数为事件对象
事件流是一个一个发过来的,客户端可以根据事件里的公钥和签名验证事件的合法性
EOSE订阅请求获取的旧数据结束,第二个参数为订阅id
客户端发起订阅时,中继会根据过滤参数读取数据库获取存档的事件,
全部发送完成后会发送一个这个消息,告诉客户端接下来的消息都是新的
定义了3个事件类型:
0 用户元数据,content为用户数据的json字符串,没有统一规范,多个客户端理应遵循大多数语义用法,这个类型的数据中继只保存一条最新的
1 文本内容,实际的社交网络的内容,即类似微博推特的正文
2 用户,content为一组中继的地址,客户端在用户保存中继列表的时候发出这条事件,用于向用户的粉丝推荐当前用户使用的中继,以防找不到用户的消息
tags数据含义:
tags的格式大致如下:
[
["e", "1234"],
["p", "123123", "123123"]
]
第二层数组里的第一个为tag的类型,第二个为值,后续的都是可选的
e代表event,即事件id
p代表pubkey,即用户ID 公钥
tags里的相同类型可以重复
02
定义了联系人列表的事件,即关注的人
事件类型为3
列表在tags里定义
[
["p", "xxx", "wss://xxx", "lucy"]
]
完整的有4个参数,第三和第四个参数可选
第二个参数为公钥,
第三个参数为中继的地址,表示在这个中继能找到这个用户的个人信息
第四个参数为用户给这个id的备注
03
定义了opentimestamps,目前不重要,略过
04
加密的直接消息,即加密私聊
事件类型为4
tags里必须包含一个p标签,表明私聊的对象
可选包含一个e标签,表明私聊里回复的消息ID
content为aes-256-cbc加密、base64编码的内容,包含base64编码的随机向量
格式为“<encrypted>?iv=<iv>”
05
定义了以人类易读的方式指向公钥
internet identifier 规范 https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1
nip-01中的0类型事件,content里可包含nip05字段标识用户的nip-05名称
格式类似邮箱 [email protected]
名称部分为 a-z0-9_ 大小写不敏感
客户端根据这个进行拆分 比如: [email protected]
名称为xxx 域名为abc.com
然后发送GET请求:
https://abc.com/.well-known/nostr.json?name=xxx
安全起见,客户端不应该跟踪这个请求的重定向
同时这个请求应该实现跨域访问
返回结果如下
{
"names": {
"xxx": "123123" // 用户公钥
},
"relays": {
"123123": ["wss://xxx"] // 公钥的信息可在这些中继找到
}
}
其中必须要验证names里的名称是否包含请求时的名称,
如果包含那就是这个用户的公钥
relays字段是可选的,不一定存在
names里可能包含多个用户的公钥
如果用户名是_,表明他是这个域名的所有者,客户端应该直接显示abc.com而不是[email protected]
06
定义了使用助记符生成私钥的规范
https://bips.xyz/39
https://bips.xyz/32
07
定义浏览器端插件的能力
通过浏览器内部支持或者插件追加的形式,使前端能直接访问window.nostr
并且提供以下能力:
async window.nostr.getPublicKey(): Promise<string> 获取用户公钥
async window.nostr.signEvent(event: Event): Promise<Event> 签名并添加id pubkey sig等字段
以下能力为可选的:
async window.nostr.getRelays(): Promise<{[url:string]: {read:boolean,write:boolean}}>
获取用户中继列表
async window.nostr.nip04.encrypt(pubkey, plaintext): Promise<string>
加密私聊消息
async window.nostr.nip04.decrypt(pubkey, ciphertext): Promise<string>
解密私聊消息
08
定义在内容中@别人的提醒
需要在tags里加上相关的p标签
有点麻烦,看原文 https://github.com/nostr-protocol/nips/blob/master/08.md
09
事件删除
事件类型为5
tags为带e标签的事件id
content为删除原因
中继会验证事件的所属人并删除这些事件,然后通知到其他连接
10
定义tags的扩展
e tag:
["e", "id", "relay", "marker"]
第三、四个为可选
第三个为中继地址,标识这个事件能在这个中继找到
第四个为标记,表明这个世界有什么作用,取值有 reply root mention
11
定义中继服务元信息的接口
使用GET请求中继并带有这个头时:
accept: application/nostr+json
中继应返回如下元数据:
{
"name": "", // 中继名称
"description": "", // 描述
"pubkey": "", // 管理员的公钥
"contact": "", // 管理员的备用联系方式
"supported_nips": [1,2,3], // 支持的NIP规范列表
"software": "", // 服务端软件
"version": "" // 版本号
}
客户端可根据这个请求判断一个服务器是否是一个nostr中继
12
定义通用tag查询,
扩展了nip-01的过滤器格式,所有"#"开头的标签都能被查询
13
定义工作量证明,类似挖矿的机制
事件中可加入一个tag:nonce 格式如下:
["nonce", "1", "20"]
第二个值为随机值
第三个值为难度
难度的定义为生成的事件id,转换为二进制,由多少个0开头
和比特币的挖矿算法相似
客户端不停计算以满足中继要求的难度,可用于对抗垃圾消息
中继可以拒绝不满足难度要求的消息
14
定义类型1事件里内容的subject标签
15
定义EOSE类型的消息 在01那里有提到
16
定义消息类型不同范围的处理方式
常规消息
1000 <= kind < 10000
这个范围的消息应该被持久化存储,并通知到相关订阅的客户端
10000 <= kind < 20000
这个范围的消息只存储一条最新的,旧的消息应该被删除
20000 <= kind < 30000
这个范围的消息不做持久化存储,应该马上转发到相关的订阅并丢弃
19
定义bech32实体编码
使用bech32编码内容,bech32格式有两部分,
前部分是人类可读的标签,
后部分是编码的内容,
通过1来分隔
这种编码不区分大小写,适合做成二维码时减少大小
具体看原文 https://github.com/nostr-protocol/nips/blob/master/19.md
定义了以下类型:
npub 公钥
nsec 私钥
note 类型1的文本
扩展的可分享的类型:
nprofile 用户资料
nevent 事件
nrelay 中继地址
naddr
20
定义事件请求结果
客户端向中继发送消息时,中继应当发送响应,格式如下:
["OK", id, true|false, ""]
第二个参数为事件id
第三个参数为true时,表示已接受,事件已保存
为false时,表示出错,事件为保存,第四个参数为错误信息
21
定义url scheme
格式为nostr:xxx
其中xxx为nip-19定义的格式
22
定义事件的created_at限制
中继会限制事件的范围,过早的或者超过当前时间太多的消息会被拒绝
23
长内容
事件id为30023,大于30000的事件类型在nip-33中定义
具体看 https://github.com/nostr-protocol/nips/blob/master/23.md
25
定义用户反应 如点赞、踩
事件类型为7
content为+ - 或别的比如emoji
tags必须带有e和p标签 表示事件和需要提醒的人
26
事件委托
比较复杂 https://github.com/nostr-protocol/nips/blob/master/26.md
可以晚点在弄
28
公共聊天
定义了事件类型:
40 创建频道
41 频道信息
42 发送的消息
43 隐藏屏蔽消息
44 禁言用户
https://github.com/nostr-protocol/nips/blob/master/28.md
33
带参数的可替换事件
类似10000 ~ 20000的事件,不同在于这种事件必须含有一个d tags
如果没有则视为 “”
所有相同的d tag的事件只会保留一份最新的
36
定义敏感内容标签
类型1的事件可带上content-warning的tag 第二个参数为原因
客户端对于含有这个标签的内容应当先隐藏内容并显示警告
用户确认后在给予展示
40
定义有效期标签
所有事件都能带上expiration tag,第二个参数为时间戳,表明什么时候过期
中继应当在过期后删除并停止传播这个事件
42
验证客户端
中继发送消息:
["AUTH", "challenge"]
客户端响应消息:
["AUTH", event]
其中事件类型为22242
tag必须包含 challenge和relay
46
nostr连接 类似oauth2
https://github.com/nostr-protocol/nips/blob/master/46.md
50
关键字过滤
REQ请求的过滤器可携带search 字段进行搜索
56
定义类型为1984的事件用于反馈举报事件或者用户
在tags里带上e和p标签
content为原因
57
闪电网络支付
58
定义徽章(没啥卵用)
65
中继列表数据
事件类型10002
tags带有r标签 表示中继列表和读写设定:
["r","wss://xxx","read|write"]
如果没有第三个参数,则代表同时可读可写
目前规范里只有这些内容,有些东西不是规范里的,但是有的客户端已经做了
简单来说只要实现了nip-01 基本大部分功能都可用了 其余都是补充和优化 提升用户体验的
目前客户端实现模式主要集中在twitter风格的用法 有部分telegram的模仿
其余的还没有太多的例子 比如
instagram
tumblr
youtube
reddit
quora
wordpress
客户端开发方向可以从这些软件的特点出发,利用nostr的协议特性来开发客户端
其他领域的可操作的空间还有:
转发机器人(twitter telegram facebook) 机器人接口
数据分析(事件、文本内容分析、点赞趋势、垃圾内容识别等)
内容发现 用户发现
垃圾过滤代理 cdn代理等
简单的游戏
小应用(剪贴板、加密文本传输)
IM聊天工具
嫁接客户端(telegram bot)
密钥管理(浏览器插件 客户端 冷钱包)
#### 字段理解
[《》,] 数组第一位参数枚举:"REQ":请求消息推送 "CLOSE":关闭消息推送
[,《》] 数组第二位参数:一个随机数,让服务端记住这一条请求是这一个客户端这个人发出的
[,,《》] 数组第三位参数为一个对象
{
kind{number} 必填,为数字。0:请求用户信息 1:动态的短文本内容 3:关注列表 4:私信 7:点赞 9735:打赏
limit{number} 取数据条数
tags{二维数组} [
[
类型:e(event id,取返回数据中的id)| p(pubkey id, 取返回数据中的pubkey) | t(话题),
值:传上面对应的值,
后面的为补充说明,如数据来源哪个中继等
]
]
}