Repository navigation
Expand file tree
/
Copy pathllms.txt
More file actions
169 lines (143 loc) · 11.4 KB
/
Copy pathllms.txt
File metadata and controls
169 lines (143 loc) · 11.4 KB
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
# nostr-veil
> Nostr has a standard for publishing trust scores (NIP-85), but ordinary
> assertions name the provider and multi-party reputation can expose a visible
> contributor graph -- making sensitive contexts like whistleblowing, abuse
> reporting, or political dissent hard to support safely.
> nostr-veil adds ring-signature privacy so scores are verifiable without
> exposing contributors.
nostr-veil is a TypeScript library for anonymous trust assertions on Nostr. It
wraps standard NIP-85 trust score events in ring signatures -- a cryptographic
technique where a verifier can confirm "someone in this group signed" without
knowing who. The output is a standard NIP-85 event that any Nostr app reads
unchanged -- privacy-aware apps can additionally verify the cryptographic
proofs via additive `veil-*` tags.
## Getting Started
```bash
npm install nostr-veil
```
```ts
import { createTrustCircle, contributeAssertion, aggregateContributions, verifyProof } from 'nostr-veil'
// 1. Define a circle of three anonymous members
const circle = createTrustCircle([alicePubkey, bobPubkey, carolPubkey])
// 2. Each member contributes independently -- identity 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. Verify: two distinct signers agreed, no names attached
const result = verifyProof(assertion)
// { valid: true, circleSize: 3, threshold: 2, distinctSigners: 2, errors: [] }
```
## Key Concepts
- **Trust circle**: A fixed group of public keys that can contribute scores anonymously. Created with `createTrustCircle()`. Minimum 2 members, maximum 1000.
- **Contribution**: A single member's anonymous vote, wrapped in a ring signature. A duplicate-detection token (key image) prevents double-voting without revealing the voter.
- **Aggregation**: Contributions are combined (median by default) into a standard NIP-85 event with `veil-ring`, `veil-threshold`, `veil-agg`, and `veil-sig` tags. Use `aggregateContributions` for kind 30382 user assertions, `aggregateEventContributions` for kind 30383 event assertions, `aggregateAddressableContributions` for kind 30384 addressable assertions, and `aggregateIdentifierContributions` for kind 30385 identifier assertions. Pass `{ proofVersion: 'v2' }` to typed contribution and aggregation helpers to bind kind and subject hint tags.
- **Verification**: `verifyProof()` confirms each ring signature is valid, duplicate-detection tokens are distinct, election IDs match, and the threshold is met -- all without learning who signed. Pass `{ requireProofVersion: 'v2' }` when a workflow must reject legacy v1 proofs.
- **Federation scope**: Circles sharing a `scope` (set via `createTrustCircle(members, { scope })`) produce matching key images for members they have in common, so `verifyFederation()` can verify several scoped events about one subject and count distinct contributors across circles without double-counting anyone in more than one.
- **Election ID**: v1 format `veil:v1:<scope-or-circleId>:<subject>`. Opt-in v2 adds assertion kind and subject hint tag/value to prevent cross-kind replay.
- **Signing**: `signEvent(template, privateKey)` is exported from the root package. It computes the event ID, signs with Schnorr, and returns a complete `SignedEvent`.
## API Surface
Two subpath exports, plus a root that re-exports everything:
**`nostr-veil/nip85`** -- NIP-85 foundation (kinds 30382-30385 + 10040):
`buildUserAssertion(pubkey, metrics)`, `buildEventAssertion(eventId, metrics)`, `buildAddressableAssertion(address, metrics)`, `buildIdentifierAssertion(identifier, kTag, metrics)`, `buildProviderDeclaration(providers, encryptedContent?)` where providers is `{ kind, metric, servicePubkey, relayHint }[]`, `parseAssertion(event)`, `parseProviderDeclaration(event, decryptFn?)`, `validateAssertion(event, options?)`, `validateAssertionStrict(event)`, `validateProviderDeclarationStrict(event)`, `assertionFilter({ kind, subject?, provider? })`, `providerFilter(pubkey)`, `NIP85_KINDS`
**`nostr-veil/proof`** -- Ring-signature proof layer:
`createTrustCircle`, `contributeAssertion`, `contributeEventAssertion`, `contributeAddressableAssertion`, `contributeIdentifierAssertion`, `aggregateContributions`, `aggregateEventContributions`, `aggregateAddressableContributions`, `aggregateIdentifierContributions`, `verifyProof`, `verifyFederation`, `canonicalMessage`, `canonicalMessageV2`, `computeCircleId`
**`nostr-veil/profiles`** -- Safer deployment profiles:
`USE_CASE_PROFILES`, `USE_CASE_PROFILE_BY_ID`, `verifyUseCaseProfile`,
`createCircleManifest`, `verifyCircleManifest`, `createDeploymentPolicy`,
`verifyDeploymentPolicy`, `createSignedDeploymentBundle`,
`verifyDeploymentBundle`, `verifyProductionDeployment`,
`createProductionDecisionReport`, `verifyProductionDeploymentReport`,
`createAdmissionChallenge`, `createAdmissionPresentation`,
`verifyAdmissionPresentation`, `verifyAdmissionRequest`,
`validateUseCaseProfileDefinition`, `VerificationIssue`,
`VerificationIssueCode`, `explainVerificationIssue`,
`remediationForIssue`, and canonical subject helpers. Run
`validateUseCaseProfileDefinition(profile)` for custom profiles to catch
NIP-85 route mismatches, unsupported metrics, missing safety metadata, and
overclaiming warnings before production use. Use
`verifyProductionDeployment` with pinned trusted publisher pubkeys for
production relay-fetched assertions; it also requires expiring bundles and
signed events by default. Use decision reports and issue explanations to turn
failures into concrete operator actions instead of only showing negative
results. Built-in profiles expose `proofClaims`, `proofLimitations`,
`requiredControls`, and `recommendedActions`; use those fields when explaining
what nostr-veil proves and what the application must still verify.
Deployment policies can also require `companionEvidence` for external facts the
proof cannot prove, such as `npm-provenance`, `sbom`,
`vulnerability-feed`, `nip05-resolution`, `https-probe`, `dns-owner-check`,
`list-revision-fetch`, `sample-review`, and `correction-channel`. Missing,
failed, stale, or wrong-subject companion evidence fails closed.
Canonical subject helpers cover Nostr-native subjects plus relays, service
endpoints, NIP-05, domains, LNURLp, NIP-96, npm packages, package artefact
digests, git repositories, GitHub repositories, maintainers, vendors, sources,
and method-scoped credential verifiers. Prefer those helpers before signing so
every contributor scores the same verifier-compatible identifier.
## Limits
nostr-veil hides which public ring member contributed. It does not hide the ring
membership list, individual anonymous metric values, network/timing metadata, or
whether the circle was socially well-chosen. Shared federation scopes intentionally
reveal cross-circle overlap by key image while still hiding the contributor's identity.
Use operational controls for the boundary: circle admission and rotation, evidence
workflow, appeals, identifier canonicalisation, expiry, revocation, batching,
transport privacy, and service-specific checks.
## Use-Case Mapping
Use `aggregateContributions` for user reputation, abuse reporting, source
corroboration, onboarding vouches, and pubkey-backed trade reputation. Use
`aggregateEventContributions` for event and claim verification. Use
`aggregateAddressableContributions` for long-form review, research, proposals,
lists, and labeler profiles. Use `aggregateIdentifierContributions` for relays,
services, packages, releases, maintainers, NIP-05 identifiers, domains, vendors,
credential verifiers, and other external identifiers; the kind 30385 `k`
namespace is chosen by the application profile, not standardised by nostr-veil.
Verifier and issuer legitimacy profiles answer "who verifies the verifier?" by
scoring a method-scoped subject such as
`verifier:proof-of-person:in-person:<pubkey>` before a community accepts
Attestr-style, NIP-VA-style, or other credential artefacts. For pubkey-bound
relay/community admission, keep the NIP-85 vouch as kind 30382 and use the
admission helpers as a separate challenge/presentation companion. The live
admission-gate example publishes that kind 30382 vouch plus a separate NIP-78
kind 30078 deployment-bundle carrier to a relay, fetches both back, and verifies
the fetched material with `verifyAdmissionRequest()`. Kind 30078 is only a
transport wrapper, not a NIP-85 assertion. For anonymous group decisions, voting,
and petitions, aggregate a `rank` ballot with `{ aggregate: 'sum' }` over
`aggregateEventContributions` for a verifiable one-vote-per-member tally; the
summed `rank` is bounded to 0-100, so it suits small electorates (boards,
committees, panels), while a petition counts distinct signatories with
`distinctSigners`. Credential co-signing, anonymous group voting, and fully
anonymous gated access remain future profiles: nostr-veil can provide the
threshold proof, but those flows need separate credential, ballot-format,
coercion-resistance, session, revocation, and transport rules.
Use-case pages are implementation profiles. Each page should identify the
subject shape, helper, metric meaning, proof version, verifier action, proof
boundary, and the operational controls needed outside nostr-veil. Caveats
should be paired with concrete controls such as evidence workflow,
canonicalisation, expiry, revocation, transport privacy, Sybil policy, or a
companion protocol.
For supported off-chain controls, prefer the companion-evidence requirement and
collector/resolver helpers over hand-written `pass` records:
`packageReleaseCompanionEvidenceRequirements()` with
`collectPackageReleaseCompanionEvidence()` or
`resolvePackageReleaseCompanionEvidence()`,
`nip05DomainCompanionEvidenceRequirements()` with
`collectNip05DomainCompanionEvidence()` or
`resolveNip05DomainCompanionEvidence()`, and
`listLabelerCompanionEvidenceRequirements()` with
`collectListLabelerCompanionEvidence()` or
`resolveListLabelerCompanionEvidence()`.
Run `npm run test:companion-evidence` for the deterministic collector smoke
test. Run `npm run test:companion-evidence:live` when you intentionally want
live npm, OSV, NIP-05, HTTPS, and relay I/O; package checks may fail closed
unless npm exposes trusted-publishing provenance and SBOM observations are
available.
## Companion Libraries
These are not dependencies but complement nostr-veil:
- [nsec-tree](https://github.com/forgesworn/nsec-tree) -- Generate separate anonymous identities from a single master key
- [canary-kit](https://github.com/forgesworn/canary-kit) -- Detect when someone is being coerced (duress signals)
- [signet](https://github.com/forgesworn/signet) -- Decentralised identity verification for Nostr
- [dominion](https://github.com/forgesworn/dominion) -- Epoch-based encrypted content access control
## Optional
- [README](https://github.com/forgesworn/nostr-veil/blob/main/README.md): Full documentation with architecture diagram
- [docs/use-cases.md](https://github.com/forgesworn/nostr-veil/blob/main/docs/use-cases.md): Concrete use-case field guide with individual worked pages and operational controls
- [IMPACT.md](https://github.com/forgesworn/nostr-veil/blob/main/IMPACT.md): Problem statement and ecosystem impact
- [@forgesworn/ring-sig](https://github.com/forgesworn/ring-sig): Underlying ring signature library