signet-protocol 1.11.2 → 1.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/badge.d.ts +5 -1
- package/dist/badge.d.ts.map +1 -1
- package/dist/badge.js +15 -6
- package/dist/badge.js.map +1 -1
- package/dist/verifiers.d.ts +13 -2
- package/dist/verifiers.d.ts.map +1 -1
- package/dist/verifiers.js +15 -2
- package/dist/verifiers.js.map +1 -1
- package/docs/implementation-levels.md +1 -1
- package/docs/signet-in-5-minutes.md +1 -1
- package/llms-full.txt +10 -8
- package/llms.txt +6 -6
- package/package.json +1 -1
- package/spec/protocol.md +2 -0
- package/src/badge.ts +22 -8
- package/src/verifiers.ts +18 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ Three things to know before you copy this:
|
|
|
51
51
|
|
|
52
52
|
- `verifySignatures` defaults to `false`. A call such as `computeBadge(pubkey, events)` checks no signatures and trusts every event it is given, so keep `{ verifySignatures: true }` unless the events were already verified. `fetchFromRelay` drops events with an invalid signature or id by default; events from any other source are not checked for you.
|
|
53
53
|
- `badge.score` is on the 0-200 Signet IQ scale but is a Level 1 approximation of `computeTrustScore(...).score`: it ignores account age and identity bridges and applies none of the per-type caps (1 professional verification, 3 in-person vouches, 5 online vouches). Use `computeTrustScore` when you need the full score.
|
|
54
|
-
- `computeBadge`
|
|
54
|
+
- `computeBadge` checks who issued a Tier 3 or Tier 4 credential only when you pass `isTrustedIssuer`, and `buildBadgeFilters` does not fetch verifier or revocation events. Without the option, any key can sign an event that carries `tier: 3` and it will count. To require an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, decide which issuers pass with `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and pass `{ isTrustedIssuer: (pk) => accepted.has(pk) }` to `computeBadge`. [llms.txt](llms.txt) has the steps.
|
|
55
55
|
|
|
56
56
|
See [Signet in 5 Minutes](docs/signet-in-5-minutes.md) for a full developer overview, or the [full flow example](examples/full-flow.ts) for all 4 tiers, trust scoring, policies, and verifier lifecycle.
|
|
57
57
|
|
package/dist/badge.d.ts
CHANGED
|
@@ -26,12 +26,16 @@ export type TrustLevel = 'unverified' | 'self-declared' | 'vouched' | 'professio
|
|
|
26
26
|
*
|
|
27
27
|
* @param pubkey - The Nostr pubkey to compute a badge for
|
|
28
28
|
* @param events - All relevant kind 31000 (`type: credential` and `type: vouch`) events
|
|
29
|
-
* @param options - Optional configuration
|
|
29
|
+
* @param options - Optional configuration. `isTrustedIssuer`, when given, is
|
|
30
|
+
* called with the signer of each Tier 3/4 or `verification-type: professional`
|
|
31
|
+
* credential; credentials whose issuer it rejects are skipped. Without it,
|
|
32
|
+
* any key's Tier 3/4 credential counts.
|
|
30
33
|
* @returns Badge info for display
|
|
31
34
|
*/
|
|
32
35
|
export declare function computeBadge(pubkey: string, events: NostrEvent[], options?: {
|
|
33
36
|
verifySignatures?: boolean;
|
|
34
37
|
now?: number;
|
|
38
|
+
isTrustedIssuer?: (issuerPubkey: string) => boolean;
|
|
35
39
|
}): Promise<BadgeInfo>;
|
|
36
40
|
/**
|
|
37
41
|
* Get the trust level classification for a badge.
|
package/dist/badge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,SAAS,GAAG,cAAc,GAAG,oBAAoB,CAAC;AAkB5G
|
|
1
|
+
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,SAAS,GAAG,cAAc,GAAG,oBAAoB,CAAC;AAkB5G;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,CAAC,EAAE;IACR,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD,GACA,OAAO,CAAC,SAAS,CAAC,CAgHpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAG1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAE/E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAYxF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAS/F"}
|
package/dist/badge.js
CHANGED
|
@@ -23,12 +23,16 @@ const TIER_TO_TRUST_LEVEL = {
|
|
|
23
23
|
*
|
|
24
24
|
* @param pubkey - The Nostr pubkey to compute a badge for
|
|
25
25
|
* @param events - All relevant kind 31000 (`type: credential` and `type: vouch`) events
|
|
26
|
-
* @param options - Optional configuration
|
|
26
|
+
* @param options - Optional configuration. `isTrustedIssuer`, when given, is
|
|
27
|
+
* called with the signer of each Tier 3/4 or `verification-type: professional`
|
|
28
|
+
* credential; credentials whose issuer it rejects are skipped. Without it,
|
|
29
|
+
* any key's Tier 3/4 credential counts.
|
|
27
30
|
* @returns Badge info for display
|
|
28
31
|
*/
|
|
29
32
|
export async function computeBadge(pubkey, events, options) {
|
|
30
33
|
const now = options?.now ?? Math.floor(Date.now() / 1000);
|
|
31
34
|
const verify = options?.verifySignatures ?? false;
|
|
35
|
+
const isTrustedIssuer = options?.isTrustedIssuer;
|
|
32
36
|
let highestTier = 1;
|
|
33
37
|
let rawScore = 0;
|
|
34
38
|
let credentialCount = 0;
|
|
@@ -61,6 +65,13 @@ export async function computeBadge(pubkey, events, options) {
|
|
|
61
65
|
// Optional signature verification
|
|
62
66
|
if (verify && !await verifyEvent(event))
|
|
63
67
|
continue;
|
|
68
|
+
const rawTier = parseInt(getTagValue(event, 'tier') || '1', 10);
|
|
69
|
+
const tier = (rawTier >= 1 && rawTier <= 4 ? rawTier : 1);
|
|
70
|
+
const verificationType = getTagValue(event, 'verification-type');
|
|
71
|
+
// Optional issuer standing: professional-grade credentials need a trusted issuer
|
|
72
|
+
if (isTrustedIssuer && (tier >= 3 || verificationType === 'professional') && !isTrustedIssuer(event.pubkey)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
64
75
|
hasAnyCredential = true;
|
|
65
76
|
credentialCount++;
|
|
66
77
|
// Capture entity type from the first credential that declares one
|
|
@@ -69,11 +80,8 @@ export async function computeBadge(pubkey, events, options) {
|
|
|
69
80
|
if (et)
|
|
70
81
|
entityType = et;
|
|
71
82
|
}
|
|
72
|
-
const rawTier = parseInt(getTagValue(event, 'tier') || '1', 10);
|
|
73
|
-
const tier = (rawTier >= 1 && rawTier <= 4 ? rawTier : 1);
|
|
74
83
|
if (tier > highestTier)
|
|
75
84
|
highestTier = tier;
|
|
76
|
-
const verificationType = getTagValue(event, 'verification-type');
|
|
77
85
|
if (verificationType === 'professional') {
|
|
78
86
|
rawScore += TRUST_WEIGHTS.PROFESSIONAL_VERIFICATION;
|
|
79
87
|
}
|
|
@@ -90,12 +98,13 @@ export async function computeBadge(pubkey, events, options) {
|
|
|
90
98
|
const subject = dTag.startsWith('vouch:') ? dTag.slice('vouch:'.length) : dTag;
|
|
91
99
|
if (subject !== pubkey)
|
|
92
100
|
continue;
|
|
93
|
-
// One vouch per voucher
|
|
101
|
+
// One vouch per voucher. Verify before claiming the slot, so a forged
|
|
102
|
+
// vouch under a voucher's pubkey cannot shadow that voucher's real one.
|
|
94
103
|
if (vouchersSeen.has(event.pubkey))
|
|
95
104
|
continue;
|
|
96
|
-
vouchersSeen.add(event.pubkey);
|
|
97
105
|
if (verify && !await verifyEvent(event))
|
|
98
106
|
continue;
|
|
107
|
+
vouchersSeen.add(event.pubkey);
|
|
99
108
|
vouchCount++;
|
|
100
109
|
const method = getTagValue(event, 'method');
|
|
101
110
|
const rawVoucherScore = parseInt(getTagValue(event, 'voucher-score') || '50', 10);
|
package/dist/badge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.js","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,2EAA2E;AAC3E,wFAAwF;AAExF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrG,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA4B9C,MAAM,WAAW,GAA+B;IAC9C,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,cAAc;IACjB,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,yBAAyB;CAC7B,CAAC;AAEF,MAAM,mBAAmB,GAAmC;IAC1D,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,cAAc;IACjB,CAAC,EAAE,oBAAoB;CACxB,CAAC;AAEF,yBAAyB;AAEzB
|
|
1
|
+
{"version":3,"file":"badge.js","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"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"}
|
package/dist/verifiers.d.ts
CHANGED
|
@@ -6,8 +6,19 @@ export declare function createVerifierCredential(privateKey: string, params: Ver
|
|
|
6
6
|
/** Parse a verifier credential event */
|
|
7
7
|
export declare function parseVerifier(event: NostrEvent): ParsedVerifier | null;
|
|
8
8
|
/** Check if a verifier has sufficient cross-verification.
|
|
9
|
-
* Requires vouches from verified professionals in different fields.
|
|
10
|
-
|
|
9
|
+
* Requires vouches from verified professionals in different fields.
|
|
10
|
+
*
|
|
11
|
+
* The spec also requires each voucher to be an active, non-revoked verifier.
|
|
12
|
+
* Pass `options.revocations` to skip vouchers that `isVerifierRevoked` reports
|
|
13
|
+
* as revoked, and `options.isActiveVerifier` to skip vouchers your client does
|
|
14
|
+
* not treat as activated. Activation is recursive (a voucher's own activation
|
|
15
|
+
* depends on its vouchers), so the library takes it as a caller-supplied
|
|
16
|
+
* predicate rather than resolving the chain itself. Without these options a
|
|
17
|
+
* voucher only needs a `type: verifier` credential with a `profession` tag. */
|
|
18
|
+
export declare function checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[], options?: {
|
|
19
|
+
revocations?: NostrEvent[];
|
|
20
|
+
isActiveVerifier?: (voucherPubkey: string) => boolean;
|
|
21
|
+
}): {
|
|
11
22
|
activated: boolean;
|
|
12
23
|
vouchCount: number;
|
|
13
24
|
professions: string[];
|
package/dist/verifiers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifiers.d.ts","sourceRoot":"","sources":["../src/verifiers.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EAEf,MAAM,YAAY,CAAC;AAEpB,kDAAkD;AAClD,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,cAAc,GACrB,aAAa,CA0Bf;AAED,4CAA4C;AAC5C,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED,wCAAwC;AACxC,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAetE;AAED;
|
|
1
|
+
{"version":3,"file":"verifiers.d.ts","sourceRoot":"","sources":["../src/verifiers.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EAEf,MAAM,YAAY,CAAC;AAEpB,kDAAkD;AAClD,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,cAAc,GACrB,aAAa,CA0Bf;AAED,4CAA4C;AAC5C,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED,wCAAwC;AACxC,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAetE;AAED;;;;;;;;;gFASgF;AAChF,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,UAAU,EAAE,EACrB,mBAAmB,EAAE,UAAU,EAAE,EACjC,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC;CACvD,GACA;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CA2DA;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,UAAU,EAAE,GACxB,OAAO,CAQT"}
|
package/dist/verifiers.js
CHANGED
|
@@ -54,8 +54,16 @@ export function parseVerifier(event) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
/** Check if a verifier has sufficient cross-verification.
|
|
57
|
-
* Requires vouches from verified professionals in different fields.
|
|
58
|
-
|
|
57
|
+
* Requires vouches from verified professionals in different fields.
|
|
58
|
+
*
|
|
59
|
+
* The spec also requires each voucher to be an active, non-revoked verifier.
|
|
60
|
+
* Pass `options.revocations` to skip vouchers that `isVerifierRevoked` reports
|
|
61
|
+
* as revoked, and `options.isActiveVerifier` to skip vouchers your client does
|
|
62
|
+
* not treat as activated. Activation is recursive (a voucher's own activation
|
|
63
|
+
* depends on its vouchers), so the library takes it as a caller-supplied
|
|
64
|
+
* predicate rather than resolving the chain itself. Without these options a
|
|
65
|
+
* voucher only needs a `type: verifier` credential with a `profession` tag. */
|
|
66
|
+
export function checkCrossVerification(verifierPubkey, vouches, verifierCredentials, options) {
|
|
59
67
|
const errors = [];
|
|
60
68
|
const professionsSet = new Set();
|
|
61
69
|
const vouchersSeen = new Set();
|
|
@@ -86,6 +94,11 @@ export function checkCrossVerification(verifierPubkey, vouches, verifierCredenti
|
|
|
86
94
|
const voucherProfession = verifierProfessions.get(vouch.pubkey);
|
|
87
95
|
if (!voucherProfession)
|
|
88
96
|
continue;
|
|
97
|
+
// Voucher must be active and not revoked, when the caller supplies the evidence
|
|
98
|
+
if (options?.revocations && isVerifierRevoked(vouch.pubkey, options.revocations))
|
|
99
|
+
continue;
|
|
100
|
+
if (options?.isActiveVerifier && !options.isActiveVerifier(vouch.pubkey))
|
|
101
|
+
continue;
|
|
89
102
|
// One vouch per voucher
|
|
90
103
|
if (vouchersSeen.has(vouch.pubkey))
|
|
91
104
|
continue;
|
package/dist/verifiers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifiers.js","sourceRoot":"","sources":["../src/verifiers.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,4DAA4D;AAE5D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AACpH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAS7D,kDAAkD;AAClD,MAAM,UAAU,kBAAkB,CAChC,cAAsB,EACtB,MAAsB;IAEtB,MAAM,UAAU,GAAe;QAC7B,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC;QACjC,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC;QACrC,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC;QAC/B,CAAC,MAAM,EAAE,MAAM,CAAC,gBAAgB,CAAC;QACjC,CAAC,MAAM,EAAE,wBAAwB,CAAC;KACnC,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,UAAU,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC;QACjC,IAAI,EAAE,iBAAiB,CAAC,QAAQ;QAChC,UAAU,EAAE,cAAc;QAC1B,OAAO,EAAE,GAAG,MAAM,CAAC,UAAU,gBAAgB,MAAM,CAAC,YAAY,EAAE;QAClE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;KAChC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,QAAQ;QACX,MAAM,EAAE,cAAc;QACtB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAAkB,EAClB,MAAsB;IAEtB,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACtC,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE1D,MAAM,SAAS,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,wBAAwB,CAAoB,CAAC;IAE9F,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE;QAClD,YAAY,EAAE,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,EAAE;QACtD,WAAW,EAAE,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE;QAChD,gBAAgB,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE;QAClD,SAAS;QACT,SAAS,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,SAAS;KAC9C,CAAC;AACJ,CAAC;AAED;
|
|
1
|
+
{"version":3,"file":"verifiers.js","sourceRoot":"","sources":["../src/verifiers.ts"],"names":[],"mappings":"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"}
|
|
@@ -79,7 +79,7 @@ The `badge` object includes:
|
|
|
79
79
|
Two things Level 1 does not do for you:
|
|
80
80
|
|
|
81
81
|
- Signature checking is opt-in. `verifySignatures` defaults to `false`, so `computeBadge(pubkey, events)` with no options trusts every event it is given. Keep `{ verifySignatures: true }` unless the events were already verified (`fetchFromRelay` drops events with an invalid signature or id by default; events from other sources are not checked for you).
|
|
82
|
-
- Issuer standing is
|
|
82
|
+
- Issuer standing is opt-in. `computeBadge` checks who issued a Tier 3 or Tier 4 credential only when you pass `isTrustedIssuer`, and `buildBadgeFilters` does not fetch verifier or revocation events. Without the option, any key can sign an event that carries `tier: 3` and it will count. To require an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, decide which issuers pass with `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and pass `{ isTrustedIssuer: (pk) => accepted.has(pk) }` to `computeBadge` (see `llms.txt`).
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
@@ -164,7 +164,7 @@ a plain object — no rendering assumptions are made.
|
|
|
164
164
|
|
|
165
165
|
- `verifySignatures` defaults to `false`. `computeBadge(pubkey, events)` with no options checks no signatures, so keep `{ verifySignatures: true }` unless the events were already verified. `fetchFromRelay` drops events with an invalid signature or id by default; events from any other source are not checked for you.
|
|
166
166
|
- `badge.score` is on the 0–200 Signet IQ scale but is a Level 1 approximation of `computeTrustScore(...).score`: it ignores account age and identity bridges and applies no per-type caps (1 professional verification, 3 in-person vouches, 5 online vouches). Use `computeTrustScore` for the full score.
|
|
167
|
-
- `computeBadge`
|
|
167
|
+
- `computeBadge` checks who issued a Tier 3 or Tier 4 credential only when you pass `isTrustedIssuer`, and `buildBadgeFilters` does not fetch verifier or revocation events. Without the option, any key can sign an event that carries `tier: 3` and it will count. To require an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, decide which issuers pass with `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and pass `{ isTrustedIssuer: (pk) => accepted.has(pk) }` to `computeBadge` (see `llms.txt`).
|
|
168
168
|
|
|
169
169
|
The `buildBadgeFilters` helper returns a standard Nostr REQ filter array ready for use with any
|
|
170
170
|
relay client library.
|
package/llms-full.txt
CHANGED
|
@@ -72,7 +72,7 @@ Default credential expiry is two years (`DEFAULT_CREDENTIAL_EXPIRY_SECONDS`) whe
|
|
|
72
72
|
- `hasEnoughVouches(vouches: NostrEvent[], subjectPubkey: string, threshold = 3, minVoucherTier: SignetTier = 2)`: `boolean`. `vouches` is raw kind 31000 vouch events (`NostrEvent[]`), not `ParsedVouch` objects. `true` when the number of qualifying vouches (one per voucher, `voucher-tier` tag ≥ `minVoucherTier`) reaches `threshold`. It does not verify signatures, and the voucher tier is the self-reported tag, not looked up. `countQualifyingVouches(vouches: NostrEvent[], subjectPubkey: string, minVoucherTier?: SignetTier)` returns the count.
|
|
73
73
|
|
|
74
74
|
### Trust & Badges
|
|
75
|
-
- `computeBadge(pubkey: string, events: NostrEvent[], opts?)`: `Promise<BadgeInfo>`. `opts: { verifySignatures?: boolean (default false); now?: number }`. `BadgeInfo: { pubkey, tier, tierLabel, score, isVerified, displayLabel, credentialCount, vouchCount, entityType? }`. `verifySignatures` defaults to `false`: with the default, `computeBadge` does not check any signature, so a call that omits it trusts every event it is given. Pass `{ verifySignatures: true }` unless the events came from `fetchFromRelay` or another source that has already dropped events with an invalid signature or id. `computeBadge` reads the `tier`, `verification-type`, `expiration`, `method` and `voucher-score` tags. It does not run `validateCredential`
|
|
75
|
+
- `computeBadge(pubkey: string, events: NostrEvent[], opts?)`: `Promise<BadgeInfo>`. `opts: { verifySignatures?: boolean (default false); now?: number; isTrustedIssuer?: (issuerPubkey: string) => boolean }`. `BadgeInfo: { pubkey, tier, tierLabel, score, isVerified, displayLabel, credentialCount, vouchCount, entityType? }`. `verifySignatures` defaults to `false`: with the default, `computeBadge` does not check any signature, so a call that omits it trusts every event it is given. Pass `{ verifySignatures: true }` unless the events came from `fetchFromRelay` or another source that has already dropped events with an invalid signature or id. `computeBadge` reads the `tier`, `verification-type`, `expiration`, `method` and `voucher-score` tags. It does not run `validateCredential`. It checks who signed a credential only when `isTrustedIssuer` is given: the predicate is called with `event.pubkey` of each credential that has `tier` 3 or 4 or `verification-type: professional`, and a credential it rejects is skipped (it adds nothing to the tier, score or `credentialCount`). Without the predicate, issuer standing is not enforced (see "Who may issue Tier 3 and Tier 4"). With `verifySignatures: true`, a vouch whose signature fails does not take its voucher's one-vouch slot.
|
|
76
76
|
- `buildBadgeFilters(pubkeys: string[])`: `Array<{ kinds: number[]; '#d': string[] }>`. One filter for kind 31000 with d-tags `credential:<pubkey>` and `vouch:<pubkey>` for each pubkey. It fetches no verifier, challenge or revocation events, so the events it returns cannot be used to check issuer standing.
|
|
77
77
|
- `meetsMinimumTier(badge: BadgeInfo, minTier: SignetTier)`: `boolean`. `true` only when `badge.isVerified` and `badge.tier >= minTier`.
|
|
78
78
|
- `computeTrustScore(subjectPubkey: string, credentials: NostrEvent[], vouches: NostrEvent[], accountCreatedAt?: number, bridges?: NostrEvent[])`: `TrustScoreBreakdown` (`{ score, tier, professionalVerifications, inPersonVouches, onlineVouches, accountAgeDays, signals }`). This is the full Signet IQ. `credentials`, `vouches` and `bridges` are raw kind 31000 events (`NostrEvent[]`), not parsed objects. It does not verify signatures and, like `computeBadge`, does not check issuer standing. `bridges` are matched by `event.pubkey === subjectPubkey` and are not ring-signature verified here: run `verifyIdentityBridge` first.
|
|
@@ -81,13 +81,13 @@ Default credential expiry is two years (`DEFAULT_CREDENTIAL_EXPIRY_SECONDS`) whe
|
|
|
81
81
|
|
|
82
82
|
### Who may issue Tier 3 and Tier 4
|
|
83
83
|
|
|
84
|
-
The library does not enforce issuer standing in `
|
|
84
|
+
The library does not enforce issuer standing in `computeTrustScore`, `verifyCredential` or `countChallengeConfirmations`, and `computeBadge` enforces it only through the `isTrustedIssuer` option. A kind 31000 `type: credential` event that carries `tier: 3` (or 4) and `verification-type: professional` counts toward the tier and the score whichever key signed it, including a key that has never published a verifier credential, and `verifySignatures: true` only proves the signer wrote the event. `buildBadgeFilters` fetches only `credential:` and `vouch:` events, so a badge built from its output cannot tell a licensed verifier from any other key. The protocol requires issuers of Tier 3 and Tier 4 to be activated, unrevoked verifiers (see `spec/protocol.md`); your client has to decide which issuers pass and give `computeBadge` that decision:
|
|
85
85
|
|
|
86
86
|
1. Fetch the candidate issuer's `type: verifier` credential, the vouches for that issuer, the verifier credentials of those vouchers, and any `type: revocation` events (all kind 31000). `buildBadgeFilters` does not fetch these, so build your own filters.
|
|
87
|
-
2. Treat an issuer as acceptable only when `checkCrossVerification(issuerPubkey, vouches, verifierCredentials).activated` is `true` and `isVerifierRevoked(issuerPubkey, revocations)` is `false`.
|
|
88
|
-
3.
|
|
87
|
+
2. Treat an issuer as acceptable only when `checkCrossVerification(issuerPubkey, vouches, verifierCredentials, { revocations, isActiveVerifier }).activated` is `true` and `isVerifierRevoked(issuerPubkey, revocations)` is `false`.
|
|
88
|
+
3. Call `computeBadge(pubkey, events, { verifySignatures: true, isTrustedIssuer: (pk) => accepted.has(pk) })`, where `accepted` holds the issuers that passed step 2.
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Voucher activation is recursive (a voucher is active only if its own vouchers are), so `checkCrossVerification` takes it as the `isActiveVerifier` predicate rather than resolving the chain. Start from verifiers your client already accepts. Without `isActiveVerifier` and `revocations`, a voucher only needs a verifier credential with a `profession` tag.
|
|
91
91
|
|
|
92
92
|
### Policies (kind 30078, NIP-78)
|
|
93
93
|
- `createPolicy(operatorPrivateKey, params)`: community verification requirements. `params: { communityId: string; adultMinTier: SignetTier; childMinTier: SignetTier; enforcement: 'client' | 'relay' | 'both'; minScore?: number; modMinTier?: SignetTier; verifierBond?: number; revocationThreshold?: number; description?: string }`. The d-tag is `signet:policy:<communityId>`.
|
|
@@ -96,7 +96,7 @@ The library does not enforce issuer standing in `computeBadge`, `computeTrustSco
|
|
|
96
96
|
|
|
97
97
|
### Verifiers (kind 31000, type: verifier)
|
|
98
98
|
- `createVerifierCredential(privateKey, params)`: register as professional verifier. `params: { profession: string; jurisdiction: string; licenceHash: string; professionalBody: string; statement?: string; bondProof?: BondProof }`.
|
|
99
|
-
- `checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[])`: activation check. `vouches` is kind 31000 vouch events (`NostrEvent[]`) and `verifierCredentials` is kind 31000 `type: verifier` events (`NostrEvent[]`), not parsed objects. Returns `{ activated: boolean; vouchCount: number; professions: string[]; errors: string[] }`. `activated` is `true` when at least `VERIFIER_ACTIVATION.MIN_VOUCHES` vouches for `verifierPubkey` come from distinct vouchers that each appear in `verifierCredentials` with a `profession` tag, and those vouchers cover at least `VERIFIER_ACTIVATION.MIN_PROFESSIONS` different professions. It does not verify signatures
|
|
99
|
+
- `checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[], options?: { revocations?: NostrEvent[]; isActiveVerifier?: (voucherPubkey: string) => boolean })`: activation check. `vouches` is kind 31000 vouch events (`NostrEvent[]`) and `verifierCredentials` is kind 31000 `type: verifier` events (`NostrEvent[]`), not parsed objects. Returns `{ activated: boolean; vouchCount: number; professions: string[]; errors: string[] }`. `activated` is `true` when at least `VERIFIER_ACTIVATION.MIN_VOUCHES` vouches for `verifierPubkey` come from distinct vouchers that each appear in `verifierCredentials` with a `profession` tag, and those vouchers cover at least `VERIFIER_ACTIVATION.MIN_PROFESSIONS` different professions. It does not verify signatures. The spec also requires each voucher to be an active, non-revoked verifier: pass `options.revocations` to skip any voucher `isVerifierRevoked` reports as revoked, and `options.isActiveVerifier` to skip vouchers your client does not treat as activated. Without these options neither is checked.
|
|
100
100
|
- `isVerifierRevoked(verifierPubkey: string, revocations: NostrEvent[])`: `boolean`. `true` when any kind 31000 `type: revocation` event in `revocations` targets `verifierPubkey`. It does not verify signatures or check who signed the revocation.
|
|
101
101
|
|
|
102
102
|
### Challenges and Revocations (kind 31000, type: challenge / revocation)
|
|
@@ -521,6 +521,8 @@ Minimum requirement for verifier activation:
|
|
|
521
521
|
- At least 2 different professions represented
|
|
522
522
|
- Vouchers must themselves be active, non-revoked verifiers
|
|
523
523
|
|
|
524
|
+
Activation is recursive: whether a voucher is active depends on its own vouchers. A client therefore resolves it from a set of verifiers it already accepts and cannot derive it from a single verifier's vouches alone. A client counting vouches (for activation or for a score) MUST check each vouch's signature before treating that voucher as seen, so that an invalid event carrying a voucher's pubkey cannot displace the voucher's valid vouch.
|
|
525
|
+
|
|
524
526
|
### Layer 3 — Rate Limiting / Anomaly Detection
|
|
525
527
|
|
|
526
528
|
All credentials are public Nostr events. Issuance volume is visible to the entire network.
|
|
@@ -3519,7 +3521,7 @@ a plain object — no rendering assumptions are made.
|
|
|
3519
3521
|
|
|
3520
3522
|
- `verifySignatures` defaults to `false`. `computeBadge(pubkey, events)` with no options checks no signatures, so keep `{ verifySignatures: true }` unless the events were already verified. `fetchFromRelay` drops events with an invalid signature or id by default; events from any other source are not checked for you.
|
|
3521
3523
|
- `badge.score` is on the 0–200 Signet IQ scale but is a Level 1 approximation of `computeTrustScore(...).score`: it ignores account age and identity bridges and applies no per-type caps (1 professional verification, 3 in-person vouches, 5 online vouches). Use `computeTrustScore` for the full score.
|
|
3522
|
-
- `computeBadge`
|
|
3524
|
+
- `computeBadge` checks who issued a Tier 3 or Tier 4 credential only when you pass `isTrustedIssuer`, and `buildBadgeFilters` does not fetch verifier or revocation events. Without the option, any key can sign an event that carries `tier: 3` and it will count. To require an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, decide which issuers pass with `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and pass `{ isTrustedIssuer: (pk) => accepted.has(pk) }` to `computeBadge` (see `llms.txt`).
|
|
3523
3525
|
|
|
3524
3526
|
The `buildBadgeFilters` helper returns a standard Nostr REQ filter array ready for use with any
|
|
3525
3527
|
relay client library.
|
|
@@ -3619,7 +3621,7 @@ The `badge` object includes:
|
|
|
3619
3621
|
Two things Level 1 does not do for you:
|
|
3620
3622
|
|
|
3621
3623
|
- Signature checking is opt-in. `verifySignatures` defaults to `false`, so `computeBadge(pubkey, events)` with no options trusts every event it is given. Keep `{ verifySignatures: true }` unless the events were already verified (`fetchFromRelay` drops events with an invalid signature or id by default; events from other sources are not checked for you).
|
|
3622
|
-
- Issuer standing is
|
|
3624
|
+
- Issuer standing is opt-in. `computeBadge` checks who issued a Tier 3 or Tier 4 credential only when you pass `isTrustedIssuer`, and `buildBadgeFilters` does not fetch verifier or revocation events. Without the option, any key can sign an event that carries `tier: 3` and it will count. To require an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, decide which issuers pass with `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and pass `{ isTrustedIssuer: (pk) => accepted.has(pk) }` to `computeBadge` (see `llms.txt`).
|
|
3623
3625
|
|
|
3624
3626
|
---
|
|
3625
3627
|
|
package/llms.txt
CHANGED
|
@@ -72,7 +72,7 @@ Default credential expiry is two years (`DEFAULT_CREDENTIAL_EXPIRY_SECONDS`) whe
|
|
|
72
72
|
- `hasEnoughVouches(vouches: NostrEvent[], subjectPubkey: string, threshold = 3, minVoucherTier: SignetTier = 2)`: `boolean`. `vouches` is raw kind 31000 vouch events (`NostrEvent[]`), not `ParsedVouch` objects. `true` when the number of qualifying vouches (one per voucher, `voucher-tier` tag ≥ `minVoucherTier`) reaches `threshold`. It does not verify signatures, and the voucher tier is the self-reported tag, not looked up. `countQualifyingVouches(vouches: NostrEvent[], subjectPubkey: string, minVoucherTier?: SignetTier)` returns the count.
|
|
73
73
|
|
|
74
74
|
### Trust & Badges
|
|
75
|
-
- `computeBadge(pubkey: string, events: NostrEvent[], opts?)`: `Promise<BadgeInfo>`. `opts: { verifySignatures?: boolean (default false); now?: number }`. `BadgeInfo: { pubkey, tier, tierLabel, score, isVerified, displayLabel, credentialCount, vouchCount, entityType? }`. `verifySignatures` defaults to `false`: with the default, `computeBadge` does not check any signature, so a call that omits it trusts every event it is given. Pass `{ verifySignatures: true }` unless the events came from `fetchFromRelay` or another source that has already dropped events with an invalid signature or id. `computeBadge` reads the `tier`, `verification-type`, `expiration`, `method` and `voucher-score` tags. It does not run `validateCredential`
|
|
75
|
+
- `computeBadge(pubkey: string, events: NostrEvent[], opts?)`: `Promise<BadgeInfo>`. `opts: { verifySignatures?: boolean (default false); now?: number; isTrustedIssuer?: (issuerPubkey: string) => boolean }`. `BadgeInfo: { pubkey, tier, tierLabel, score, isVerified, displayLabel, credentialCount, vouchCount, entityType? }`. `verifySignatures` defaults to `false`: with the default, `computeBadge` does not check any signature, so a call that omits it trusts every event it is given. Pass `{ verifySignatures: true }` unless the events came from `fetchFromRelay` or another source that has already dropped events with an invalid signature or id. `computeBadge` reads the `tier`, `verification-type`, `expiration`, `method` and `voucher-score` tags. It does not run `validateCredential`. It checks who signed a credential only when `isTrustedIssuer` is given: the predicate is called with `event.pubkey` of each credential that has `tier` 3 or 4 or `verification-type: professional`, and a credential it rejects is skipped (it adds nothing to the tier, score or `credentialCount`). Without the predicate, issuer standing is not enforced (see "Who may issue Tier 3 and Tier 4"). With `verifySignatures: true`, a vouch whose signature fails does not take its voucher's one-vouch slot.
|
|
76
76
|
- `buildBadgeFilters(pubkeys: string[])`: `Array<{ kinds: number[]; '#d': string[] }>`. One filter for kind 31000 with d-tags `credential:<pubkey>` and `vouch:<pubkey>` for each pubkey. It fetches no verifier, challenge or revocation events, so the events it returns cannot be used to check issuer standing.
|
|
77
77
|
- `meetsMinimumTier(badge: BadgeInfo, minTier: SignetTier)`: `boolean`. `true` only when `badge.isVerified` and `badge.tier >= minTier`.
|
|
78
78
|
- `computeTrustScore(subjectPubkey: string, credentials: NostrEvent[], vouches: NostrEvent[], accountCreatedAt?: number, bridges?: NostrEvent[])`: `TrustScoreBreakdown` (`{ score, tier, professionalVerifications, inPersonVouches, onlineVouches, accountAgeDays, signals }`). This is the full Signet IQ. `credentials`, `vouches` and `bridges` are raw kind 31000 events (`NostrEvent[]`), not parsed objects. It does not verify signatures and, like `computeBadge`, does not check issuer standing. `bridges` are matched by `event.pubkey === subjectPubkey` and are not ring-signature verified here: run `verifyIdentityBridge` first.
|
|
@@ -81,13 +81,13 @@ Default credential expiry is two years (`DEFAULT_CREDENTIAL_EXPIRY_SECONDS`) whe
|
|
|
81
81
|
|
|
82
82
|
### Who may issue Tier 3 and Tier 4
|
|
83
83
|
|
|
84
|
-
The library does not enforce issuer standing in `
|
|
84
|
+
The library does not enforce issuer standing in `computeTrustScore`, `verifyCredential` or `countChallengeConfirmations`, and `computeBadge` enforces it only through the `isTrustedIssuer` option. A kind 31000 `type: credential` event that carries `tier: 3` (or 4) and `verification-type: professional` counts toward the tier and the score whichever key signed it, including a key that has never published a verifier credential, and `verifySignatures: true` only proves the signer wrote the event. `buildBadgeFilters` fetches only `credential:` and `vouch:` events, so a badge built from its output cannot tell a licensed verifier from any other key. The protocol requires issuers of Tier 3 and Tier 4 to be activated, unrevoked verifiers (see `spec/protocol.md`); your client has to decide which issuers pass and give `computeBadge` that decision:
|
|
85
85
|
|
|
86
86
|
1. Fetch the candidate issuer's `type: verifier` credential, the vouches for that issuer, the verifier credentials of those vouchers, and any `type: revocation` events (all kind 31000). `buildBadgeFilters` does not fetch these, so build your own filters.
|
|
87
|
-
2. Treat an issuer as acceptable only when `checkCrossVerification(issuerPubkey, vouches, verifierCredentials).activated` is `true` and `isVerifierRevoked(issuerPubkey, revocations)` is `false`.
|
|
88
|
-
3.
|
|
87
|
+
2. Treat an issuer as acceptable only when `checkCrossVerification(issuerPubkey, vouches, verifierCredentials, { revocations, isActiveVerifier }).activated` is `true` and `isVerifierRevoked(issuerPubkey, revocations)` is `false`.
|
|
88
|
+
3. Call `computeBadge(pubkey, events, { verifySignatures: true, isTrustedIssuer: (pk) => accepted.has(pk) })`, where `accepted` holds the issuers that passed step 2.
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Voucher activation is recursive (a voucher is active only if its own vouchers are), so `checkCrossVerification` takes it as the `isActiveVerifier` predicate rather than resolving the chain. Start from verifiers your client already accepts. Without `isActiveVerifier` and `revocations`, a voucher only needs a verifier credential with a `profession` tag.
|
|
91
91
|
|
|
92
92
|
### Policies (kind 30078, NIP-78)
|
|
93
93
|
- `createPolicy(operatorPrivateKey, params)`: community verification requirements. `params: { communityId: string; adultMinTier: SignetTier; childMinTier: SignetTier; enforcement: 'client' | 'relay' | 'both'; minScore?: number; modMinTier?: SignetTier; verifierBond?: number; revocationThreshold?: number; description?: string }`. The d-tag is `signet:policy:<communityId>`.
|
|
@@ -96,7 +96,7 @@ The library does not enforce issuer standing in `computeBadge`, `computeTrustSco
|
|
|
96
96
|
|
|
97
97
|
### Verifiers (kind 31000, type: verifier)
|
|
98
98
|
- `createVerifierCredential(privateKey, params)`: register as professional verifier. `params: { profession: string; jurisdiction: string; licenceHash: string; professionalBody: string; statement?: string; bondProof?: BondProof }`.
|
|
99
|
-
- `checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[])`: activation check. `vouches` is kind 31000 vouch events (`NostrEvent[]`) and `verifierCredentials` is kind 31000 `type: verifier` events (`NostrEvent[]`), not parsed objects. Returns `{ activated: boolean; vouchCount: number; professions: string[]; errors: string[] }`. `activated` is `true` when at least `VERIFIER_ACTIVATION.MIN_VOUCHES` vouches for `verifierPubkey` come from distinct vouchers that each appear in `verifierCredentials` with a `profession` tag, and those vouchers cover at least `VERIFIER_ACTIVATION.MIN_PROFESSIONS` different professions. It does not verify signatures
|
|
99
|
+
- `checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[], options?: { revocations?: NostrEvent[]; isActiveVerifier?: (voucherPubkey: string) => boolean })`: activation check. `vouches` is kind 31000 vouch events (`NostrEvent[]`) and `verifierCredentials` is kind 31000 `type: verifier` events (`NostrEvent[]`), not parsed objects. Returns `{ activated: boolean; vouchCount: number; professions: string[]; errors: string[] }`. `activated` is `true` when at least `VERIFIER_ACTIVATION.MIN_VOUCHES` vouches for `verifierPubkey` come from distinct vouchers that each appear in `verifierCredentials` with a `profession` tag, and those vouchers cover at least `VERIFIER_ACTIVATION.MIN_PROFESSIONS` different professions. It does not verify signatures. The spec also requires each voucher to be an active, non-revoked verifier: pass `options.revocations` to skip any voucher `isVerifierRevoked` reports as revoked, and `options.isActiveVerifier` to skip vouchers your client does not treat as activated. Without these options neither is checked.
|
|
100
100
|
- `isVerifierRevoked(verifierPubkey: string, revocations: NostrEvent[])`: `boolean`. `true` when any kind 31000 `type: revocation` event in `revocations` targets `verifierPubkey`. It does not verify signatures or check who signed the revocation.
|
|
101
101
|
|
|
102
102
|
### Challenges and Revocations (kind 31000, type: challenge / revocation)
|
package/package.json
CHANGED
package/spec/protocol.md
CHANGED
|
@@ -365,6 +365,8 @@ Minimum requirement for verifier activation:
|
|
|
365
365
|
- At least 2 different professions represented
|
|
366
366
|
- Vouchers must themselves be active, non-revoked verifiers
|
|
367
367
|
|
|
368
|
+
Activation is recursive: whether a voucher is active depends on its own vouchers. A client therefore resolves it from a set of verifiers it already accepts and cannot derive it from a single verifier's vouches alone. A client counting vouches (for activation or for a score) MUST check each vouch's signature before treating that voucher as seen, so that an invalid event carrying a voucher's pubkey cannot displace the voucher's valid vouch.
|
|
369
|
+
|
|
368
370
|
### Layer 3 — Rate Limiting / Anomaly Detection
|
|
369
371
|
|
|
370
372
|
All credentials are public Nostr events. Issuance volume is visible to the entire network.
|
package/src/badge.ts
CHANGED
|
@@ -54,16 +54,24 @@ const TIER_TO_TRUST_LEVEL: Record<SignetTier, TrustLevel> = {
|
|
|
54
54
|
*
|
|
55
55
|
* @param pubkey - The Nostr pubkey to compute a badge for
|
|
56
56
|
* @param events - All relevant kind 31000 (`type: credential` and `type: vouch`) events
|
|
57
|
-
* @param options - Optional configuration
|
|
57
|
+
* @param options - Optional configuration. `isTrustedIssuer`, when given, is
|
|
58
|
+
* called with the signer of each Tier 3/4 or `verification-type: professional`
|
|
59
|
+
* credential; credentials whose issuer it rejects are skipped. Without it,
|
|
60
|
+
* any key's Tier 3/4 credential counts.
|
|
58
61
|
* @returns Badge info for display
|
|
59
62
|
*/
|
|
60
63
|
export async function computeBadge(
|
|
61
64
|
pubkey: string,
|
|
62
65
|
events: NostrEvent[],
|
|
63
|
-
options?: {
|
|
66
|
+
options?: {
|
|
67
|
+
verifySignatures?: boolean;
|
|
68
|
+
now?: number;
|
|
69
|
+
isTrustedIssuer?: (issuerPubkey: string) => boolean;
|
|
70
|
+
}
|
|
64
71
|
): Promise<BadgeInfo> {
|
|
65
72
|
const now = options?.now ?? Math.floor(Date.now() / 1000);
|
|
66
73
|
const verify = options?.verifySignatures ?? false;
|
|
74
|
+
const isTrustedIssuer = options?.isTrustedIssuer;
|
|
67
75
|
|
|
68
76
|
let highestTier: SignetTier = 1;
|
|
69
77
|
let rawScore = 0;
|
|
@@ -95,6 +103,15 @@ export async function computeBadge(
|
|
|
95
103
|
// Optional signature verification
|
|
96
104
|
if (verify && !await verifyEvent(event)) continue;
|
|
97
105
|
|
|
106
|
+
const rawTier = parseInt(getTagValue(event, 'tier') || '1', 10);
|
|
107
|
+
const tier = (rawTier >= 1 && rawTier <= 4 ? rawTier : 1) as SignetTier;
|
|
108
|
+
const verificationType = getTagValue(event, 'verification-type');
|
|
109
|
+
|
|
110
|
+
// Optional issuer standing: professional-grade credentials need a trusted issuer
|
|
111
|
+
if (isTrustedIssuer && (tier >= 3 || verificationType === 'professional') && !isTrustedIssuer(event.pubkey)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
hasAnyCredential = true;
|
|
99
116
|
credentialCount++;
|
|
100
117
|
|
|
@@ -104,11 +121,8 @@ export async function computeBadge(
|
|
|
104
121
|
if (et) entityType = et;
|
|
105
122
|
}
|
|
106
123
|
|
|
107
|
-
const rawTier = parseInt(getTagValue(event, 'tier') || '1', 10);
|
|
108
|
-
const tier = (rawTier >= 1 && rawTier <= 4 ? rawTier : 1) as SignetTier;
|
|
109
124
|
if (tier > highestTier) highestTier = tier;
|
|
110
125
|
|
|
111
|
-
const verificationType = getTagValue(event, 'verification-type');
|
|
112
126
|
if (verificationType === 'professional') {
|
|
113
127
|
rawScore += TRUST_WEIGHTS.PROFESSIONAL_VERIFICATION;
|
|
114
128
|
}
|
|
@@ -125,11 +139,11 @@ export async function computeBadge(
|
|
|
125
139
|
const subject = dTag.startsWith('vouch:') ? dTag.slice('vouch:'.length) : dTag;
|
|
126
140
|
if (subject !== pubkey) continue;
|
|
127
141
|
|
|
128
|
-
// One vouch per voucher
|
|
142
|
+
// One vouch per voucher. Verify before claiming the slot, so a forged
|
|
143
|
+
// vouch under a voucher's pubkey cannot shadow that voucher's real one.
|
|
129
144
|
if (vouchersSeen.has(event.pubkey)) continue;
|
|
130
|
-
vouchersSeen.add(event.pubkey);
|
|
131
|
-
|
|
132
145
|
if (verify && !await verifyEvent(event)) continue;
|
|
146
|
+
vouchersSeen.add(event.pubkey);
|
|
133
147
|
|
|
134
148
|
vouchCount++;
|
|
135
149
|
|
package/src/verifiers.ts
CHANGED
|
@@ -75,11 +75,23 @@ export function parseVerifier(event: NostrEvent): ParsedVerifier | null {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/** Check if a verifier has sufficient cross-verification.
|
|
78
|
-
* Requires vouches from verified professionals in different fields.
|
|
78
|
+
* Requires vouches from verified professionals in different fields.
|
|
79
|
+
*
|
|
80
|
+
* The spec also requires each voucher to be an active, non-revoked verifier.
|
|
81
|
+
* Pass `options.revocations` to skip vouchers that `isVerifierRevoked` reports
|
|
82
|
+
* as revoked, and `options.isActiveVerifier` to skip vouchers your client does
|
|
83
|
+
* not treat as activated. Activation is recursive (a voucher's own activation
|
|
84
|
+
* depends on its vouchers), so the library takes it as a caller-supplied
|
|
85
|
+
* predicate rather than resolving the chain itself. Without these options a
|
|
86
|
+
* voucher only needs a `type: verifier` credential with a `profession` tag. */
|
|
79
87
|
export function checkCrossVerification(
|
|
80
88
|
verifierPubkey: string,
|
|
81
89
|
vouches: NostrEvent[],
|
|
82
|
-
verifierCredentials: NostrEvent[]
|
|
90
|
+
verifierCredentials: NostrEvent[],
|
|
91
|
+
options?: {
|
|
92
|
+
revocations?: NostrEvent[];
|
|
93
|
+
isActiveVerifier?: (voucherPubkey: string) => boolean;
|
|
94
|
+
}
|
|
83
95
|
): {
|
|
84
96
|
activated: boolean;
|
|
85
97
|
vouchCount: number;
|
|
@@ -115,6 +127,10 @@ export function checkCrossVerification(
|
|
|
115
127
|
const voucherProfession = verifierProfessions.get(vouch.pubkey);
|
|
116
128
|
if (!voucherProfession) continue;
|
|
117
129
|
|
|
130
|
+
// Voucher must be active and not revoked, when the caller supplies the evidence
|
|
131
|
+
if (options?.revocations && isVerifierRevoked(vouch.pubkey, options.revocations)) continue;
|
|
132
|
+
if (options?.isActiveVerifier && !options.isActiveVerifier(vouch.pubkey)) continue;
|
|
133
|
+
|
|
118
134
|
// One vouch per voucher
|
|
119
135
|
if (vouchersSeen.has(vouch.pubkey)) continue;
|
|
120
136
|
vouchersSeen.add(vouch.pubkey);
|