forgesworn/nostr-veil
GitHub: forgesworn/nostr-veil
基于 LSAG 环签名为 Nostr 的 Web of Trust 信誉体系提供匿名可验证的信任评分机制,解决信任评分中评分者身份暴露的隐私问题。
Stars: 2 | Forks: 0
# nostr-veil
[](https://github.com/forgesworn/nostr-veil/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/nostr-veil)
[](./LICENCE)
[](https://github.com/sponsors/TheCryptoDonkey)
**你可以验证信任分数,却无需知晓是谁给出的。**
这是一个为 Nostr 信誉系统打造的隐私层。一群人可以共同为某人的可信度打分,任何人都可以验证结果——但没人能确定究竟是哪些成员参与了打分。专为滥用行为举报、吹哨举报、新闻调查和匿名同行评审而构建。
## 核心概念
如果你刚接触这个领域,以下是这些术语的通俗解释:
| 术语 | 通俗解释 |
|------|--------------|
| **Web of Trust (WoT)** | 不再由中央机构决定谁值得信任,而是由人们互相担保。你的声誉建立在认识你的人的评价之上。 |
| **NIP-85** | 一种用于发布信任分数的 [Nostr 标准](https://github.com/nostr-protocol/nips/blob/master/85.md)(例如“此人有 800 名关注者,排名为 74”)。可以将其视为声誉数据的通用格式。 |
| **环签名** | 一种密码学技术,允许某人代表一个群组对消息进行签名。验证者可以确认“该群组中的某个人签署了此消息”,但无法确定具体是谁。就像密封的选票——你可以统计选票,但无法追溯来源。 |
| **LSAG** | Linkable Spontaneous Anonymous Group signature(可链接自发匿名群组签名)——一种特殊类型的环签名,还能检测重复投票。如果同一个人试图投两次票,系统会将其揪出,同时不会暴露其身份。 |
| **信任圈** | 共同产出信任分数的群体。每个成员匿名贡献分数;结果将被合并(默认取中位数)。 |
## 信任三元悖论
当今的 Nostr 信任分数 (NIP-85) 迫使你只能兼顾以下两者:
| 特性 | 标准 NIP-85 | 使用 nostr-veil |
|-------------|:---:|:---:|
| 可验证 | ✓ | ✓ |
| 隐私性 | ✗ | ✓ |
| 可移植性 | ✓ | ✓ |
任何能看到分数的人,都能确切地看到是谁给出的。这对于良性的社交信号来说没有问题。但一旦涉及敏感主题——如滥用举报、吹哨爆料、政治异见——这套机制就会彻底失效。最需要声誉系统的人,往往正是那些承担不起暴露身份风险的人。
nostr-veil 解决了这三个问题。其输出是一个标准的 NIP-85 事件,任何现有的 Nostr 应用都能显示。理解 nostr-veil 的应用则可以更进一步,验证支撑这些分数的密码学证明。
## 架构
```
graph TB
subgraph Consumers["Nostr Clients"]
any["Any NIP-85 client
reads standard assertions"] veil["Veil-aware client
verifies ring proofs"] end subgraph Veil["nostr-veil"] proof["nostr-veil/proof
Ring signatures
Anonymous contributions
Threshold aggregation"] nip85["nostr-veil/nip85
Trust score events
Provider declarations
Builders · Parsers · Filters"] end subgraph Crypto["Cryptographic Primitives"] ringsig["@forgesworn/ring-sig
Ring signature engine"] noble["@noble/curves + hashes
Cryptographic primitives"] end subgraph Companions["Companion Libraries"] nsec["nsec-tree
sub-identity derivation"] canary["canary-kit
coercion detection"] signet["signet
identity verification"] end any -->|"read"| nip85 veil -->|"verify"| proof proof -->|"builds on"| nip85 proof --> ringsig nip85 --> noble nsec -.->|"personas for"| proof canary -.->|"liveness for"| proof style any fill:#3b82f6,color:#fff style veil fill:#d97706,color:#000 style proof fill:#d97706,color:#000 style nip85 fill:#0d9488,color:#fff style ringsig fill:#8b5cf6,color:#fff style noble fill:#8b5cf6,color:#fff style nsec fill:#374151,color:#e5e7eb style canary fill:#374151,color:#e5e7eb style signet fill:#374151,color:#e5e7eb ``` ## 快速开始 ``` npm install nostr-veil ``` ``` import { createTrustCircle, contributeAssertion, aggregateContributions, verifyProof } from 'nostr-veil' // 1. Define the circle (three anonymous members) const circle = createTrustCircle([alicePubkey, bobPubkey, carolPubkey]) // 2. Each member contributes independently -- their identity is hidden inside the ring const alice = contributeAssertion(circle, subjectPubkey, { followers: 820, rank: 74 }, alicePrivkey, 0) const bob = contributeAssertion(circle, subjectPubkey, { followers: 900, rank: 80 }, bobPrivkey, 1) // 3. Aggregate into a standard NIP-85 kind 30382 event const assertion = aggregateContributions(circle, subjectPubkey, [alice, bob]) // 4. Any client verifies -- two distinct members agreed, no names attached const result = verifyProof(assertion) // { valid: true, circleSize: 3, threshold: 2, distinctSigners: 2, errors: [] } ``` **重要提示:** `memberIndex` 必须与成员在*经过排序的* pubkey 数组中的位置相匹配。`createTrustCircle` 会按字典序对 pubkeys 进行排序——你传递给 `contributeAssertion` 的索引必须反映排序后的顺序,而不是你传递给 `createTrustCircle` 的原始顺序。请使用 `circle.members.indexOf(myPubkey)` 来查找正确的索引。 最终生成的 `assertion` 是一个普通的 `EventTemplate`,你可以像对待任何其他 Nostr 事件一样对其进行签名和发布。 ## API 参考 ### `nostr-veil/nip85` -- NIP-85 基础 | 导出 | 描述 | |--------|-------------| | `buildUserAssertion(pubkey, metrics)` | 构建 kind 30382 用户断言事件模板 | | `buildEventAssertion(eventId, metrics)` | 构建 kind 30383 事件断言 | | `buildAddressableAssertion(address, metrics)` | 构建 kind 30384 可寻址断言 | | `buildIdentifierAssertion(identifier, kTag, metrics)` | 构建 kind 30385 标识符断言 | | `buildProviderDeclaration(providers, encryptedContent?)` | 构建 kind 10040 提供者声明 | | `parseAssertion(event)` | 将原始事件解析为 `ParsedAssertion` | | `parseProviderDeclaration(event, decryptFn?)` | 将 kind 10040 提供者声明解析为 `ParsedProvider[]`(支持可选的 NIP-44 解密) | | `validateAssertion(event)` | 验证 NIP-85 断言 -- 返回 `{ valid, errors }` | | `assertionFilter({ kind, subject?, provider? })` | 构建用于断言的 relay 查询过滤器 | | `providerFilter(pubkey)` | 构建用于提供者声明的 relay 查询过滤器 | | `NIP85_KINDS` | 种类常量:`USER`、`EVENT`、`ADDRESSABLE`、`IDENTIFIER`、`PROVIDER` | ### `nostr-veil/proof` -- 环签名证明层 | 导出 | 描述 | |--------|-------------| | `createTrustCircle(memberPubkeys)` | 从 pubkey 数组创建信任圈 | | `contributeAssertion(circle, subject, metrics, privateKey, memberIndex)` | 生成匿名环签名的 `Contribution` | | `aggregateContributions(circle, subject, contributions, options?)` | 将贡献聚合为带有 veil 标签的 NIP-85 事件(默认聚合方式:中位数) | | `verifyProof(event, options?)` | 验证环签名、阈值元数据以及已签名的指标聚合 | | `canonicalMessage(circleId, subject, metrics)` | 计算由贡献者签名的规范消息 | | `computeCircleId(sortedPubkeys)` | 计算确定性的圈子 ID(以冒号连接的 pubkeys 的 SHA-256 值) | ### 签名工具(根导出) | 导出 | 描述 | |--------|-------------| | `signEvent(template, privateKey)` | 使用 BIP-340 Schnorr 签名未签名的事件模板 -- 返回完整的 `SignedEvent` | | `computeEventId(event)` | 计算 NIP-01 事件 ID(规范序列化的 SHA-256 值) | ## 工作原理 信任圈中的每个成员独立提交他们的分数。在底层,每次提交都被包裹在环签名中——这是一种密码学信封,用于证明“该群组中的某位成员签署了此消息”,同时不会暴露具体是哪位成员。 发布的事件在标准 NIP-85 格式的基础上额外携带了三个标签: - `veil-ring` -- 圈子成员的完整列表(可能参与贡献的群组) - `veil-threshold` -- 实际参与的成员数量与圈子总规模的对比 - `veil-sig`(每位贡献者一个)-- 环签名和重复检测 token 验证者调用 `verifyProof`,该函数将: 1. 检查每个环签名是否有效(由真实成员签署) 2. 检查重复检测 token 是否各不相同(没有人投两次票) 3. 确认阈值元数据是否与环和独立签名相匹配 4. 确认发布的指标标签是否与已签名贡献的聚合结果相匹配 验证过程自始至终都不需要知道是哪位成员生成了哪个签名。群组成员身份是公开的。实际贡献者的身份则是隐匿的。 默认情况下,验证过程期望使用与 `aggregateContributions` 相同的中位数聚合方式。如果你向 `aggregateContributions` 传递了自定义的 `aggregateFn`,请务必将相同的函数传递给 `verifyProof`。 ## 伴生项目 nostr-veil 是更广泛的身份与信任技术栈中的一个层级: - [@forgesworn/ring-sig](https://github.com/forgesworn/ring-sig) -- 环签名引擎(核心密码学) - [nsec-tree](https://github.com/forgesworn/nsec-tree) -- 从单个主密钥生成独立的匿名身份 - [canary-kit](https://github.com/forgesworn/canary-kit) -- 检测某人是否正在受到胁迫(胁迫信号) - [signet](https://github.com/forgesworn/signet) -- Nostr 的去中心化身份验证 - [dominion](https://github.com/forgesworn/dominion) -- 基于周期的加密内容访问控制 每个项目均独立维护和发布。nostr-veil 仅专注于匿名信任断言。 ## 延伸阅读 - [IMPACT.md](./IMPACT.md) -- 问题描述与生态影响 - [CONTRIBUTING.md](./CONTRIBUTING.md) -- 设置、测试与 PR 指南 - [SECURITY.md](./SECURITY.md) -- 漏洞报告与密码学范围 - [llms.txt](./llms.txt) -- 面向 LLM 的机器可读项目摘要 - [CLAUDE.md](./CLAUDE.md) -- 用于贡献的 AI 代理指令 ## ForgeSworn 工具包的一部分 [ForgeSworn](https://forgesworn.dev) 为 Nostr 构建开源的密码学身份、支付和协同工具。 | 库 | 功能 | |---------|-------------| | [nsec-tree](https://github.com/forgesworn/nsec-tree) | 确定性子身份派生 | | [ring-sig](https://github.com/forgesworn/ring-sig) | 环签名引擎(核心密码学) | | [range-proof](https://github.com/forgesworn/range-proof) | Pedersen 承诺范围证明 | | [canary-kit](https://github.com/forgesworn/canary-kit) | 抗胁迫语音验证 | | [spoken-token](https://github.com/forgesworn/spoken-token) | 人类可读的验证 token | | [toll-booth](https://github.com/forgesworn/toll-booth) | L402 支付中间件 | | [geohash-kit](https://github.com/forgesworn/geohash-kit) | 带多边形覆盖的 Geohash 工具包 | | [nostr-attestations](https://github.com/forgesworn/nostr-attestations) | NIP-VA 可验证认证 | | [dominion](https://github.com/forgesworn/dominion) | 基于周期的加密访问控制 | | [nostr-veil](https://github.com/forgesworn/nostr-veil) | 隐私保护的 Web of Trust | ## 许可证 MIT
reads standard assertions"] veil["Veil-aware client
verifies ring proofs"] end subgraph Veil["nostr-veil"] proof["nostr-veil/proof
Ring signatures
Anonymous contributions
Threshold aggregation"] nip85["nostr-veil/nip85
Trust score events
Provider declarations
Builders · Parsers · Filters"] end subgraph Crypto["Cryptographic Primitives"] ringsig["@forgesworn/ring-sig
Ring signature engine"] noble["@noble/curves + hashes
Cryptographic primitives"] end subgraph Companions["Companion Libraries"] nsec["nsec-tree
sub-identity derivation"] canary["canary-kit
coercion detection"] signet["signet
identity verification"] end any -->|"read"| nip85 veil -->|"verify"| proof proof -->|"builds on"| nip85 proof --> ringsig nip85 --> noble nsec -.->|"personas for"| proof canary -.->|"liveness for"| proof style any fill:#3b82f6,color:#fff style veil fill:#d97706,color:#000 style proof fill:#d97706,color:#000 style nip85 fill:#0d9488,color:#fff style ringsig fill:#8b5cf6,color:#fff style noble fill:#8b5cf6,color:#fff style nsec fill:#374151,color:#e5e7eb style canary fill:#374151,color:#e5e7eb style signet fill:#374151,color:#e5e7eb ``` ## 快速开始 ``` npm install nostr-veil ``` ``` import { createTrustCircle, contributeAssertion, aggregateContributions, verifyProof } from 'nostr-veil' // 1. Define the circle (three anonymous members) const circle = createTrustCircle([alicePubkey, bobPubkey, carolPubkey]) // 2. Each member contributes independently -- their identity is hidden inside the ring const alice = contributeAssertion(circle, subjectPubkey, { followers: 820, rank: 74 }, alicePrivkey, 0) const bob = contributeAssertion(circle, subjectPubkey, { followers: 900, rank: 80 }, bobPrivkey, 1) // 3. Aggregate into a standard NIP-85 kind 30382 event const assertion = aggregateContributions(circle, subjectPubkey, [alice, bob]) // 4. Any client verifies -- two distinct members agreed, no names attached const result = verifyProof(assertion) // { valid: true, circleSize: 3, threshold: 2, distinctSigners: 2, errors: [] } ``` **重要提示:** `memberIndex` 必须与成员在*经过排序的* pubkey 数组中的位置相匹配。`createTrustCircle` 会按字典序对 pubkeys 进行排序——你传递给 `contributeAssertion` 的索引必须反映排序后的顺序,而不是你传递给 `createTrustCircle` 的原始顺序。请使用 `circle.members.indexOf(myPubkey)` 来查找正确的索引。 最终生成的 `assertion` 是一个普通的 `EventTemplate`,你可以像对待任何其他 Nostr 事件一样对其进行签名和发布。 ## API 参考 ### `nostr-veil/nip85` -- NIP-85 基础 | 导出 | 描述 | |--------|-------------| | `buildUserAssertion(pubkey, metrics)` | 构建 kind 30382 用户断言事件模板 | | `buildEventAssertion(eventId, metrics)` | 构建 kind 30383 事件断言 | | `buildAddressableAssertion(address, metrics)` | 构建 kind 30384 可寻址断言 | | `buildIdentifierAssertion(identifier, kTag, metrics)` | 构建 kind 30385 标识符断言 | | `buildProviderDeclaration(providers, encryptedContent?)` | 构建 kind 10040 提供者声明 | | `parseAssertion(event)` | 将原始事件解析为 `ParsedAssertion` | | `parseProviderDeclaration(event, decryptFn?)` | 将 kind 10040 提供者声明解析为 `ParsedProvider[]`(支持可选的 NIP-44 解密) | | `validateAssertion(event)` | 验证 NIP-85 断言 -- 返回 `{ valid, errors }` | | `assertionFilter({ kind, subject?, provider? })` | 构建用于断言的 relay 查询过滤器 | | `providerFilter(pubkey)` | 构建用于提供者声明的 relay 查询过滤器 | | `NIP85_KINDS` | 种类常量:`USER`、`EVENT`、`ADDRESSABLE`、`IDENTIFIER`、`PROVIDER` | ### `nostr-veil/proof` -- 环签名证明层 | 导出 | 描述 | |--------|-------------| | `createTrustCircle(memberPubkeys)` | 从 pubkey 数组创建信任圈 | | `contributeAssertion(circle, subject, metrics, privateKey, memberIndex)` | 生成匿名环签名的 `Contribution` | | `aggregateContributions(circle, subject, contributions, options?)` | 将贡献聚合为带有 veil 标签的 NIP-85 事件(默认聚合方式:中位数) | | `verifyProof(event, options?)` | 验证环签名、阈值元数据以及已签名的指标聚合 | | `canonicalMessage(circleId, subject, metrics)` | 计算由贡献者签名的规范消息 | | `computeCircleId(sortedPubkeys)` | 计算确定性的圈子 ID(以冒号连接的 pubkeys 的 SHA-256 值) | ### 签名工具(根导出) | 导出 | 描述 | |--------|-------------| | `signEvent(template, privateKey)` | 使用 BIP-340 Schnorr 签名未签名的事件模板 -- 返回完整的 `SignedEvent` | | `computeEventId(event)` | 计算 NIP-01 事件 ID(规范序列化的 SHA-256 值) | ## 工作原理 信任圈中的每个成员独立提交他们的分数。在底层,每次提交都被包裹在环签名中——这是一种密码学信封,用于证明“该群组中的某位成员签署了此消息”,同时不会暴露具体是哪位成员。 发布的事件在标准 NIP-85 格式的基础上额外携带了三个标签: - `veil-ring` -- 圈子成员的完整列表(可能参与贡献的群组) - `veil-threshold` -- 实际参与的成员数量与圈子总规模的对比 - `veil-sig`(每位贡献者一个)-- 环签名和重复检测 token 验证者调用 `verifyProof`,该函数将: 1. 检查每个环签名是否有效(由真实成员签署) 2. 检查重复检测 token 是否各不相同(没有人投两次票) 3. 确认阈值元数据是否与环和独立签名相匹配 4. 确认发布的指标标签是否与已签名贡献的聚合结果相匹配 验证过程自始至终都不需要知道是哪位成员生成了哪个签名。群组成员身份是公开的。实际贡献者的身份则是隐匿的。 默认情况下,验证过程期望使用与 `aggregateContributions` 相同的中位数聚合方式。如果你向 `aggregateContributions` 传递了自定义的 `aggregateFn`,请务必将相同的函数传递给 `verifyProof`。 ## 伴生项目 nostr-veil 是更广泛的身份与信任技术栈中的一个层级: - [@forgesworn/ring-sig](https://github.com/forgesworn/ring-sig) -- 环签名引擎(核心密码学) - [nsec-tree](https://github.com/forgesworn/nsec-tree) -- 从单个主密钥生成独立的匿名身份 - [canary-kit](https://github.com/forgesworn/canary-kit) -- 检测某人是否正在受到胁迫(胁迫信号) - [signet](https://github.com/forgesworn/signet) -- Nostr 的去中心化身份验证 - [dominion](https://github.com/forgesworn/dominion) -- 基于周期的加密内容访问控制 每个项目均独立维护和发布。nostr-veil 仅专注于匿名信任断言。 ## 延伸阅读 - [IMPACT.md](./IMPACT.md) -- 问题描述与生态影响 - [CONTRIBUTING.md](./CONTRIBUTING.md) -- 设置、测试与 PR 指南 - [SECURITY.md](./SECURITY.md) -- 漏洞报告与密码学范围 - [llms.txt](./llms.txt) -- 面向 LLM 的机器可读项目摘要 - [CLAUDE.md](./CLAUDE.md) -- 用于贡献的 AI 代理指令 ## ForgeSworn 工具包的一部分 [ForgeSworn](https://forgesworn.dev) 为 Nostr 构建开源的密码学身份、支付和协同工具。 | 库 | 功能 | |---------|-------------| | [nsec-tree](https://github.com/forgesworn/nsec-tree) | 确定性子身份派生 | | [ring-sig](https://github.com/forgesworn/ring-sig) | 环签名引擎(核心密码学) | | [range-proof](https://github.com/forgesworn/range-proof) | Pedersen 承诺范围证明 | | [canary-kit](https://github.com/forgesworn/canary-kit) | 抗胁迫语音验证 | | [spoken-token](https://github.com/forgesworn/spoken-token) | 人类可读的验证 token | | [toll-booth](https://github.com/forgesworn/toll-booth) | L402 支付中间件 | | [geohash-kit](https://github.com/forgesworn/geohash-kit) | 带多边形覆盖的 Geohash 工具包 | | [nostr-attestations](https://github.com/forgesworn/nostr-attestations) | NIP-VA 可验证认证 | | [dominion](https://github.com/forgesworn/dominion) | 基于周期的加密访问控制 | | [nostr-veil](https://github.com/forgesworn/nostr-veil) | 隐私保护的 Web of Trust | ## 许可证 MIT
标签:LSAG, NIP-85, Nostr, NPM包, OSV-Scalibr, TypeScript, Web of Trust, WoT, 举报滥用, 信任评分, 区块链, 匿名同行评审, 匿名群签名, 去中心化社交, 可验证性, 吹哨人, 声誉系统, 安全插件, 密码学, 手动系统调用, 数据可视化, 新闻业, 暗色界面, 环签名, 网络安全, 自动化攻击, 防女巫攻击, 隐私保护, 隐私层, 零知识证明