signet-protocol 1.11.2 → 1.12.0

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 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` does not check who issued a Tier 3 or Tier 4 credential, and `buildBadgeFilters` does not fetch verifier or revocation events. Any key can sign an event that carries `tier: 3`, and it will count. To require that the issuer is an activated, unrevoked verifier, fetch the verifier credentials, their vouches and revocations yourself, apply `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and drop other issuers' Tier 3 and Tier 4 credentials before calling `computeBadge`. [llms.txt](llms.txt) has the steps.
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.
@@ -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;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,OAAO,CAAC,SAAS,CAAC,CAyGpB;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"}
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;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,MAAoB,EACpB,OAAsD;IAEtD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,EAAE,gBAAgB,IAAI,KAAK,CAAC;IAElD,IAAI,WAAW,GAAe,CAAC,CAAC;IAChC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,UAAkC,CAAC;IAEvC,sBAAsB;IACtB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,SAAS;QAC9C,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,iBAAiB,CAAC,UAAU;YAAE,SAAS;QAC1E,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,OAAe,CAAC;QACpB,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC;YAC1C,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrF,CAAC;QACD,IAAI,OAAO,KAAK,MAAM;YAAE,SAAS;QAEjC,wEAAwE;QACxE,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACjD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG;gBAAE,SAAS;QACxC,CAAC;QAED,kCAAkC;QAClC,IAAI,MAAM,IAAI,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC;YAAE,SAAS;QAElD,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAElB,kEAAkE;QAClE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,aAAa,CAA2B,CAAC;YACvE,IAAI,EAAE;gBAAE,UAAU,GAAG,EAAE,CAAC;QAC1B,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAe,CAAC;QACxE,IAAI,IAAI,GAAG,WAAW;YAAE,WAAW,GAAG,IAAI,CAAC;QAE3C,MAAM,gBAAgB,GAAG,WAAW,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACjE,IAAI,gBAAgB,KAAK,cAAc,EAAE,CAAC;YACxC,QAAQ,IAAI,aAAa,CAAC,yBAAyB,CAAC;QACtD,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,SAAS;QAC9C,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,iBAAiB,CAAC,KAAK;YAAE,SAAS;QACrE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,IAAI,OAAO,KAAK,MAAM;YAAE,SAAS;QAEjC,wBAAwB;QACxB,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,SAAS;QAC7C,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE/B,IAAI,MAAM,IAAI,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC;YAAE,SAAS;QAElD,UAAU,EAAE,CAAC;QAEb,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC5C,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QAClF,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC;QAC3G,MAAM,UAAU,GAAG,YAAY,GAAG,eAAe,CAAC;QAElD,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,QAAQ,IAAI,aAAa,CAAC,eAAe,GAAG,UAAU,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,QAAQ,IAAI,aAAa,CAAC,YAAY,GAAG,UAAU,CAAC;QACtD,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAE3C,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC,gBAAgB,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QAC1C,YAAY,GAAG,YAAY,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,GAAG,SAAS,UAAU,WAAW,GAAG,CAAC;IACtD,CAAC;IAED,OAAO;QACL,MAAM;QACN,IAAI,EAAE,WAAW;QACjB,SAAS;QACT,KAAK;QACL,UAAU,EAAE,gBAAgB;QAC5B,YAAY;QACZ,eAAe;QACf,UAAU;QACV,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgB;IAC5C,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IACrE,OAAO,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgB,EAAE,OAAmB;IACpE,OAAO,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,MAAoB;IACxE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,OAAO,KAAK,CAAC;QAClD,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,SAAS,KAAK,iBAAiB,CAAC,UAAU,IAAI,SAAS,KAAK,iBAAiB,CAAC,KAAK,EAAE,CAAC;YACxF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,8CAA8C;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9E,OAAO,OAAO,KAAK,MAAM,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAiB;IACjD,2EAA2E;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,cAAc,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACzE,OAAO;QACL;YACE,KAAK,EAAE,CAAC,gBAAgB,CAAC;YACzB,IAAI,EAAE,KAAK;SACZ;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"badge.js","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"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"}
@@ -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
- export declare function checkCrossVerification(verifierPubkey: string, vouches: NostrEvent[], verifierCredentials: NostrEvent[]): {
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[];
@@ -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;wEACwE;AACxE,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,UAAU,EAAE,EACrB,mBAAmB,EAAE,UAAU,EAAE,GAChC;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAuDA;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,UAAU,EAAE,GACxB,OAAO,CAQT"}
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
- export function checkCrossVerification(verifierPubkey, vouches, verifierCredentials) {
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;
@@ -1 +1 @@
1
- {"version":3,"file":"verifiers.js","sourceRoot":"","sources":["../src/verifiers.ts"],"names":[],"mappings":"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"}
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;;;;;;;;;gFASgF;AAChF,MAAM,UAAU,sBAAsB,CACpC,cAAsB,EACtB,OAAqB,EACrB,mBAAiC,EACjC,OAGC;IAOD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,+CAA+C;IAC/C,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;YAAE,SAAS;QAC7C,IAAI,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,iBAAiB,CAAC,QAAQ;YAAE,SAAS;QACvE,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACnD,IAAI,UAAU,EAAE,CAAC;YACf,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,+DAA+D;IAC/D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,SAAS;QAC9C,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,iBAAiB,CAAC,KAAK;YAAE,SAAS;QAErE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,IAAI,OAAO,KAAK,cAAc;YAAE,SAAS;QAEzC,qDAAqD;QACrD,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChE,IAAI,CAAC,iBAAiB;YAAE,SAAS;QAEjC,gFAAgF;QAChF,IAAI,OAAO,EAAE,WAAW,IAAI,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC;YAAE,SAAS;QAC3F,IAAI,OAAO,EAAE,gBAAgB,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,SAAS;QAEnF,wBAAwB;QACxB,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,SAAS;QAC7C,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE/B,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACtC,UAAU,EAAE,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,SAAS,GACb,UAAU,IAAI,mBAAmB,CAAC,WAAW;QAC7C,WAAW,CAAC,MAAM,IAAI,mBAAmB,CAAC,eAAe,CAAC;IAE5D,IAAI,UAAU,GAAG,mBAAmB,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,CAAC,IAAI,CACT,QAAQ,mBAAmB,CAAC,WAAW,8CAA8C,UAAU,EAAE,CAClG,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,GAAG,mBAAmB,CAAC,eAAe,EAAE,CAAC;QAC7D,MAAM,CAAC,IAAI,CACT,qBAAqB,mBAAmB,CAAC,eAAe,gCAAgC,WAAW,CAAC,MAAM,EAAE,CAC7G,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACxD,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,iBAAiB,CAC/B,cAAsB,EACtB,WAAyB;IAEzB,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QAC9B,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,iBAAiB,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC5E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxF,OAAO,MAAM,KAAK,cAAc,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -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 not checked. `computeBadge` does not look at who signed a Tier 3 or Tier 4 credential, and `buildBadgeFilters` fetches no verifier or revocation events, so 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, apply `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and drop other issuers' Tier 3 and Tier 4 credentials before calling `computeBadge` (see `llms.txt`).
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` does not check who issued a Tier 3 or Tier 4 credential, and `buildBadgeFilters` fetches no verifier or revocation events. 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, apply `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and drop other issuers' Tier 3 and Tier 4 credentials before calling `computeBadge` (see `llms.txt`).
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` and does not check who signed a credential, so it does not enforce issuer standing (see "Who may issue Tier 3 and Tier 4").
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 `computeBadge`, `computeTrustScore`, `verifyCredential` or `countChallengeConfirmations`. 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 apply that rule before calling `computeBadge`:
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. Drop every Tier 3 and Tier 4 credential whose `event.pubkey` is not an acceptable issuer, then pass the remaining events to `computeBadge`.
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
- `checkCrossVerification` does not check that each voucher is itself activated or unrevoked, so filter `verifierCredentials` the same way before step 2 if you need the spec's full rule.
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, and it does not check that a voucher is itself activated or unrevoked (the spec asks for that, the function does not do it), so filter `verifierCredentials` to activated, unrevoked verifiers yourself.
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` does not check who issued a Tier 3 or Tier 4 credential, and `buildBadgeFilters` fetches no verifier or revocation events. 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, apply `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and drop other issuers' Tier 3 and Tier 4 credentials before calling `computeBadge` (see `llms.txt`).
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 not checked. `computeBadge` does not look at who signed a Tier 3 or Tier 4 credential, and `buildBadgeFilters` fetches no verifier or revocation events, so 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, apply `checkCrossVerification(...).activated` and `isVerifierRevoked(...)`, and drop other issuers' Tier 3 and Tier 4 credentials before calling `computeBadge` (see `llms.txt`).
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` and does not check who signed a credential, so it does not enforce issuer standing (see "Who may issue Tier 3 and Tier 4").
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 `computeBadge`, `computeTrustScore`, `verifyCredential` or `countChallengeConfirmations`. 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 apply that rule before calling `computeBadge`:
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. Drop every Tier 3 and Tier 4 credential whose `event.pubkey` is not an acceptable issuer, then pass the remaining events to `computeBadge`.
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
- `checkCrossVerification` does not check that each voucher is itself activated or unrevoked, so filter `verifierCredentials` the same way before step 2 if you need the spec's full rule.
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, and it does not check that a voucher is itself activated or unrevoked (the spec asks for that, the function does not do it), so filter `verifierCredentials` to activated, unrevoked verifiers yourself.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signet-protocol",
3
- "version": "1.11.2",
3
+ "version": "1.12.0",
4
4
  "description": "Decentralised identity verification protocol for Nostr",
5
5
  "homepage": "https://github.com/forgesworn/signet#readme",
6
6
  "type": "module",
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?: { verifySignatures?: boolean; now?: number }
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);