signet-protocol 1.11.1 → 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 CHANGED
@@ -39,6 +39,7 @@ const events = await fetchFromRelay('wss://relay.example.com', filters);
39
39
  const badge = await computeBadge('<hex-pubkey>', events, { verifySignatures: true });
40
40
 
41
41
  // badge.tier => 3, badge.score => 106, badge.displayLabel => "Verified (Tier 3)"
42
+ // (illustrative values; badge.score is a Level 1 approximation, see the notes below)
42
43
  if (meetsMinimumTier(badge, 2)) {
43
44
  // allow posting in verified-only community
44
45
  }
@@ -46,9 +47,15 @@ if (meetsMinimumTier(badge, 2)) {
46
47
 
47
48
  `fetchFromRelay(relayUrl, filters)` is the package's own minimal relay client and returns `Promise<NostrEvent[]>`. Any other Nostr client works too: pass `buildBadgeFilters` output as the REQ filters and hand the resulting events to `computeBadge`. `computeBadge` is async and returns `Promise<BadgeInfo>`.
48
49
 
50
+ Three things to know before you copy this:
51
+
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
+ - `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` 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
+
49
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.
50
57
 
51
- For LLM and agent tooling, [llms.txt](llms.txt) is a concise API overview and [llms-full.txt](llms-full.txt) adds the full protocol and voting specifications and the developer guides in one file. Both ship in the npm package.
58
+ For LLM and agent tooling, [llms.txt](llms.txt) is a concise API overview and [llms-full.txt](llms-full.txt) adds the full protocol and voting specifications and the developer guides in one file. Both ship in the npm package, as do `spec/`, `examples/` and the four `docs/` pages linked from this README and `llms.txt` (`signet-in-5-minutes.md`, `implementation-levels.md`, `private-vault-v1.md` and `bot-ownership-v1.md`), so those relative links resolve inside `node_modules/signet-protocol/`. Other files under `docs/`, `legal/` and `tests/` stay in the repository only.
52
59
 
53
60
  ## Protocol
54
61
 
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":"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"}
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":"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"}
@@ -0,0 +1,43 @@
1
+ # Draft bot ownership profile
2
+
3
+ Unstable. Import from `signet-protocol/experimental`; nothing here is exported
4
+ from the package root, and any of it may change without a deprecation period.
5
+
6
+ This unreleased profile uses nostr-attestations kind 31000 with type
7
+ `bot-ownership`. Its replaceable d-tag is `bot-ownership:<bot pubkey>`, p-tag is
8
+ the bot, and the event author is the selected owner persona. It cannot replace
9
+ a credential or vouch. Ownership never grants the bot the owner's trust.
10
+
11
+ A live claim has `valid_from` equal to `created_at`, matching `valid_to` and
12
+ `expiration`, and content `{ "v": 1, "label": "…" }` (100 characters maximum).
13
+ Labels refuse C0/C1 controls, DEL, zero-width and bidi marks (LRM, RLM, ALM),
14
+ line/paragraph separators and bidi embeddings, overrides and isolates, lone
15
+ surrogates, and every other Unicode format or default-ignorable code point,
16
+ except the narrow in-context forms emoji sequences need: the zero-width
17
+ joiner between emoji elements, the VS15/VS16 variation selectors right after
18
+ an emoji or a keycap base, and the England/Scotland/Wales RGI subdivision
19
+ flag tag sequences (no other tag sequence, and no hidden tag payload).
20
+ The event supplies principal, agent, issue date and expiry without requiring a
21
+ raw-hash signature: ordinary NIP-46 sign_event works on hardware. Revocation is
22
+ the existing same-address attestation with `status=revoked` and empty content.
23
+
24
+ Builders return unsigned events and never publish. Product integration must ask
25
+ for creation consent every time, sign with the chosen persona, and keep the
26
+ signed claim private until publication is explicitly chosen. Hardware-derived
27
+ bots must be registered on the device; this module does not register them.
28
+
29
+ Default expiry is 30 days, permitted creation range 1–90 days. Readers apply the
30
+ earliest of signed expiry, 90 days and an optional stricter per-bot ceiling,
31
+ with five minutes of clock tolerance. Lapsed is different from revoked.
32
+ Renewal becomes due with ten days left; retry at most daily. The scheduling
33
+ helper never signs: local policy and hardware approvals remain in force.
34
+
35
+ Consumers pin the expected owner and bot, verify signatures, and select the
36
+ newest same-address event while retaining their previously seen high-water mark.
37
+ A stale ownership claim must not resurrect a newer revocation. This module
38
+ validates one event; it does not implement relay freshness or fetch ordering.
39
+ `readBotOwnershipSync` applies identical validation for synchronous storage and
40
+ transport codecs; `readBotOwnership` is the asynchronous form of the same check.
41
+
42
+ Protocol review, app lifecycle/UI, isolated bots contacts, downstream adoption,
43
+ and hardware acceptance are still required before release.
@@ -0,0 +1,235 @@
1
+ # Signet Implementation Levels
2
+
3
+ A guide for Nostr client developers integrating the Signet identity protocol. There are three progressive integration levels. Start at Level 1 and add depth as your use case demands.
4
+
5
+ ## Summary
6
+
7
+ | Level | Effort | Event Kinds | New Crypto | What Users Get |
8
+ |-------|--------|-------------|------------|----------------|
9
+ | 1 — Read Trust Badges | ~1 weekend | 31000 (credential, vouch) | None | Tier badges and Signet Score on profiles |
10
+ | 2 — Issue Vouches | A few days | 31000, 30078 | None | Peer vouching, Tier 2 networks, policy checks |
11
+ | 3 — Full Protocol | Weeks to months | 31000, 30078, 30482-30484 | Merkle, Pedersen range proofs, ring signatures | Professional verification, ZK proofs, elections |
12
+
13
+ ---
14
+
15
+ ## Level 1 — Read Trust Badges
16
+
17
+ **Effort:** A weekend
18
+ **Event kinds:** 31000 (attestation — filter by `type` tag: `credential`, `vouch`)
19
+ **New crypto required:** None — Nostr already verifies Schnorr signatures
20
+
21
+ This is the NIP-05 equivalent of Signet integration. You read one event kind (filtered by type tag), call one function, and display a badge. No cryptographic work on your part beyond what your Nostr client already does.
22
+
23
+ ### What to build
24
+
25
+ 1. Subscribe to kind `31000` events with `type` tags `credential` and `vouch` for the pubkeys you want to display.
26
+ 2. Pass the events to `computeBadge()`.
27
+ 3. Show the result on the user's profile.
28
+
29
+ ### Installation
30
+
31
+ ```bash
32
+ npm install signet-protocol
33
+ ```
34
+
35
+ ### Code example
36
+
37
+ ```typescript
38
+ import { computeBadge, buildBadgeFilters, fetchFromRelay } from 'signet-protocol';
39
+
40
+ const pubkey = '<hex-pubkey>';
41
+
42
+ // 1. Fetch events from a relay (fetchFromRelay is the package's own client; any Nostr client works)
43
+ const filters = buildBadgeFilters([pubkey]);
44
+ // The filters request kind 31000 with credential and vouch type tags.
45
+ const events = await fetchFromRelay('wss://relay.example.com', filters);
46
+
47
+ // 2. Compute badge from the events you receive
48
+ const badge = await computeBadge(pubkey, events, { verifySignatures: true }); // async: returns Promise<BadgeInfo>
49
+
50
+ // 3. Display
51
+ if (badge.isVerified) {
52
+ console.log(badge.tierLabel, badge.score); // replace with your own rendering
53
+ }
54
+ ```
55
+
56
+ The `badge` object includes:
57
+
58
+ - `badge.isVerified` — boolean, true if the pubkey has any Signet verification
59
+ - `badge.tier` — number 1–4, the highest verified tier
60
+ - `badge.tierLabel` — human-readable string (e.g. `"Vouched"`, `"Verified"`, `"Verified (Child Safety)"`)
61
+ - `badge.score`: integer 0–200 on the Signet IQ scale (100 = government standard), derived from credentials and vouches only. It is a Level 1 approximation of `computeTrustScore(...).score`: it ignores account age and identity bridges and applies no per-type caps
62
+
63
+ ### Trust tiers at a glance
64
+
65
+ | Tier | Meaning |
66
+ |------|---------|
67
+ | 1 | Self-declared — the user has made identity claims |
68
+ | 2 | Web-of-trust — other users have vouched for this identity |
69
+ | 3 | Professional adult — verified by a recognised professional body |
70
+ | 4 | Professional adult + child safeguarding — extended professional verification |
71
+
72
+ ### What you do NOT need to handle at this level
73
+
74
+ - Generating or validating zero-knowledge proofs
75
+ - Nullifier computation
76
+ - Merkle trees
77
+ - Any cryptographic primitives beyond standard Nostr signature verification
78
+
79
+ Two things Level 1 does not do for you:
80
+
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 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
+
84
+ ---
85
+
86
+ ## Level 2 — Issue Vouches
87
+
88
+ **Effort:** A few days
89
+ **Event kinds:** 31000 (attestation — credential, vouch types), 30078 (policy)
90
+ **New crypto required:** None
91
+
92
+ Level 2 adds the ability for your users to vouch for each other and for your client to respect community policies. This is the viral layer of the protocol — peer vouching is what builds Tier 2 trust networks and creates network effects across clients.
93
+
94
+ ### What to build
95
+
96
+ Everything from Level 1, plus:
97
+
98
+ 1. UI for a user to select a contact and publish a kind `31000` vouch attestation (with `['type', 'vouch']` tag).
99
+ 2. Read kind `30078` (NIP-78 app-specific data) policies from community relays and check whether a pubkey meets the policy threshold before allowing sensitive actions.
100
+
101
+ ### Creating a vouch
102
+
103
+ ```typescript
104
+ import { createVouch } from 'signet-protocol';
105
+
106
+ // createVouch takes the voucher's private key and a VouchParams object.
107
+ // It returns a signed Nostr event ready to publish.
108
+ const vouchEvent = await createVouch(myPrivkey, {
109
+ subjectPubkey: theirPubkey,
110
+ method: 'in-person',
111
+ context: 'I have met this person in person and verified their identity.',
112
+ voucherTier: 2,
113
+ voucherScore: 106,
114
+ });
115
+
116
+ // Publish to your relay as a standard Nostr event.
117
+ await publishEvent(vouchEvent);
118
+ ```
119
+
120
+ ### Parsing incoming vouches
121
+
122
+ ```typescript
123
+ import { parseVouch, countQualifyingVouches, hasEnoughVouches } from 'signet-protocol';
124
+
125
+ // Parse a raw Nostr event into a structured vouch object
126
+ const vouch = parseVouch(rawEvent);
127
+
128
+ // Count how many qualifying vouches a pubkey has received
129
+ const count = countQualifyingVouches(allVouchEvents, pubkey);
130
+
131
+ // Check whether a pubkey clears the threshold required for Tier 2
132
+ const qualifies = hasEnoughVouches(allVouchEvents, pubkey, 3);
133
+ ```
134
+
135
+ Reference: `src/vouches.ts`
136
+
137
+ ### Checking policy compliance
138
+
139
+ Policies (kind `30078`, NIP-78) let community operators define trust thresholds for specific actions — for example, requiring a minimum score of 40 before a user can post to a paid relay, or requiring Tier 2 before joining a group.
140
+
141
+ ```typescript
142
+ import { parsePolicy, checkPolicyCompliance, PolicyChecker } from 'signet-protocol';
143
+
144
+ // Parse a policy event
145
+ const policy = parsePolicy(policyEvent);
146
+
147
+ // One-shot check: does this user meet the policy?
148
+ const result = checkPolicyCompliance(policy, userTier, userScore);
149
+ if (!result.allowed) {
150
+ showError(`This action requires Tier ${result.requiredTier} or higher.`);
151
+ }
152
+
153
+ // For repeated checks across many pubkeys, use PolicyChecker (more efficient)
154
+ const checker = new PolicyChecker(policyEvent);
155
+ const adultResult = checker.checkAdult(userTier, userScore);
156
+ ```
157
+
158
+ Reference: `src/policies.ts`
159
+
160
+ ### What you do NOT need to handle at this level
161
+
162
+ - Zero-knowledge proofs of any kind
163
+ - Professional body verification flows
164
+ - Two-credential ceremonies
165
+ - Nullifiers or document hashing
166
+
167
+ ---
168
+
169
+ ## Level 3 — Full Protocol
170
+
171
+ **Effort:** Weeks to months
172
+ **Event kinds:** 31000 (all attestation types), 30078 (policies), 30482-30484 (voting)
173
+ **New crypto required:** Merkle trees, Pedersen range proofs (age range proofs), linkable ring signatures
174
+
175
+ Level 3 is full reference implementation. This is appropriate for clients that want to act as professional verifiers, run elections, or issue and receive Tier 3/4 credentials with zero-knowledge proofs. Elections themselves (kinds 30482–30484) live in the separate `nostr-anon-vote` package, not in `signet-protocol`.
176
+
177
+ ### What to build (beyond Level 2)
178
+
179
+ - **Two-credential ceremony:** Professional verifiers issue a Natural Person credential (kind `31000` with `type: credential`, nullifier and Merkle root) and an anonymous Persona credential simultaneously. The Natural Person credential binds to a document-based nullifier (`SHA-256(docType || country || docNumber || salt)`) to prevent duplicate identity claims. The Persona credential reveals only an age range, not the full identity.
180
+ - **Merkle selective disclosure:** Verified attributes are stored as private leaves of a Merkle tree. Only the root is published on-chain. Users disclose individual leaves with inclusion proofs when required.
181
+ - **Age range proofs:** Pedersen range proofs prove that a user is within an age range without revealing their exact date of birth.
182
+ - **Professional verifier onboarding:** Verifier accounts (kind `31000` with `type: verifier`) are backed by recognised professional bodies (law societies, medical boards, notary commissions). The protocol has no central authority — trust anchors are these external bodies.
183
+ - **Ring signatures and elections:** The voting extension (kinds `30482`–`30484`) uses linkable ring signatures for anonymous voting with double-spend prevention. It is specified in `spec/voting.md` and implemented in the separate `nostr-anon-vote` package; `signet-protocol` ships only the linkable ring signature primitives (`lsagSign`, `lsagVerify`, `computeKeyImage`).
184
+ - **Guardian delegation:** Delegation attestations (kind `31000` with `type: delegation`) with scopes (`full`, `activity-approval`, `content-management`, `contact-approval`) support family structures and guardian-linked sub-accounts.
185
+ - **Revocation and lifecycle:** Revocation attestations (kind `31000` with `type: revocation`) and credential chains using `supersedes`/`superseded-by` tags handle name changes, document renewal, and tier upgrades.
186
+ - **Identity bridging:** Identity bridge attestations (kind `31000` with `type: identity-bridge`) link verified identities across protocols or jurisdictions.
187
+ - **Anomaly detection:** The library includes heuristics for detecting suspicious vouching patterns (e.g. vouch rings, rapid bulk vouching).
188
+
189
+ ### Key modules in signet-protocol
190
+
191
+ All modules are re-exported from the package root. You can also import them directly for tree-shaking.
192
+
193
+ | Module | Source | Responsibility |
194
+ |--------|--------|----------------|
195
+ | Credentials | `src/credentials.ts` | Issue, parse, verify credentials including Merkle roots and nullifiers |
196
+ | Ring signatures | `src/ring-signature.ts` | Ring signature generation and verification (via `@forgesworn/ring-sig`) |
197
+ | Range proofs | `src/range-proof.ts` | Pedersen range proofs for age verification (via `@forgesworn/range-proof`) |
198
+ | Merkle trees | `src/merkle.ts` | Build attribute trees, generate and verify inclusion proofs |
199
+ | Signet Score | `src/trust-score.ts` | Weighted signal aggregation, 0–200 Signet Score computation |
200
+ | Anomaly detection | `src/anomaly.ts` | Detect vouch ring attacks and other manipulation patterns |
201
+ | Jurisdictions | `jurisdiction-kit` | Jurisdiction-specific rules for document types and professional bodies |
202
+
203
+ All modules are re-exported from the package root (`signet-protocol`). Import from the root entry point:
204
+
205
+ ### Event kind reference
206
+
207
+ All Signet identity attestations use a single generic kind, differentiated by `type` tag:
208
+
209
+ | Kind | Type Tag | Name | Description |
210
+ |------|----------|------|-------------|
211
+ | 31000 | `credential` | Credential | Identity claim with optional Merkle root and nullifier |
212
+ | 31000 | `vouch` | Vouch | Peer attestation of another pubkey's identity |
213
+ | 31000 | `verifier` | Verifier | Professional verifier registration |
214
+ | 31000 | `challenge` | Challenge | Challenge against a verifier's legitimacy |
215
+ | 31000 | `revocation` | Revocation | Credential or vouch revocation |
216
+ | 31000 | `identity-bridge` | Identity Bridge | Cross-protocol or cross-jurisdiction identity link |
217
+ | 31000 | `delegation` | Delegation | Guardian or organisational delegation with scopes |
218
+ | 30078 | — | Policy | Community trust threshold definitions (NIP-78) |
219
+ | 30482 | — | Election | Voting election definition (separate NIP) |
220
+ | 30483 | — | Ballot | Anonymous signed ballot |
221
+ | 30484 | — | Election Result | Tallied election result |
222
+
223
+ **Note:** Kind 31000 is the NIP-VA (Verifiable Attestations) kind. The `type` tag differentiates attestation types within the single kind.
224
+
225
+ ### Where to start
226
+
227
+ The `signet-protocol` npm package contains everything. The protocol specification at `spec/protocol.md` is the authoritative source of truth for all behaviour.
228
+
229
+ ---
230
+
231
+ ## Choosing your level
232
+
233
+ Start at Level 1 unless you have a specific reason to go further. Most social clients only need Level 1 — displaying trust context on profiles. Level 2 is appropriate if your community has a peer moderation culture. Level 3 is for infrastructure builders: relay operators, professional verification portals, and election platforms.
234
+
235
+ You can ship Level 1 in a weekend and upgrade later. The three levels are strictly additive — nothing you build at Level 1 needs to be undone to reach Level 2 or 3.
@@ -0,0 +1,104 @@
1
+ # Private vault checkpoint v1 (staged, not released)
2
+
3
+ Unstable. Import from `signet-protocol/experimental`; nothing here is exported
4
+ from the package root, and any of it may change without a deprecation period.
5
+
6
+ Keys derive flat from the tree root using `vaultPurpose`; index means rotation.
7
+ Legacy Natural Person rails remain permanently readable. No writer is switched
8
+ until both the app and Sapwood can restore the new dataset.
9
+
10
+ The existing v2 AES-GCM/NIP-44 envelope is retained. Vault keys wrap content keys
11
+ and sign only control events below 12 KiB of UTF-8 content. Every chunk is sealed
12
+ to the vault key but signed by an installation device key. A checkpoint authorises
13
+ that device and lists exact chunk event IDs, encrypted-content SHA-256 hashes,
14
+ byte lengths and order. The complete plaintext SHA-256 is the revision. Recovery discovers and merges all
15
+ device heads rather than selecting one global latest snapshot, so concurrent
16
+ offline writers cannot replace each other. The reader fails closed beyond 16
17
+ heads instead of dropping devices; head compaction is future work. Readers
18
+ verify signatures, scope, bounds, sequence, every chunk and the revision before
19
+ returning any data. Missing chunks never mean an empty dataset.
20
+
21
+ All events use existing kind 30078. Each installation retains its own checkpoint under
22
+ `vaultCheckpointTag(author, publisherDevicePubkey)`;
23
+ chunks use a content-derived tag and are fetched by exact event ID. This reuses
24
+ the existing event kind rather than allocating a new kind. Content-addressed
25
+ chunk replacement cannot alter an already signed checkpoint reference. No public
26
+ tags name the dataset or connect device chunks to the vault author. Routing tags
27
+ are opaque, not secret: knowing a vault pubkey lets an observer compute its tag.
28
+
29
+ Head discovery reads only kind-30078 events by the vault author whose single
30
+ d-tag has the checkpoint shape (32 lowercase hex characters); other events by
31
+ the vault key are ignored (by the relay reader and again by recovery) before
32
+ any limit applies and are never decrypted. The
33
+ shape is necessary, not sufficient, because the publisher is encrypted: the
34
+ vault key must sign no other kind-30078 event with a 32-hex d-tag, or that
35
+ event is read as a malformed head and the read is unusable.
36
+
37
+ Checkpoints dated more than `VAULT_CLOCK_TOLERANCE_SECONDS` (300) after the
38
+ reader's clock are rejected; every reader takes `now` as a parameter. A device
39
+ whose clock ran ahead therefore cannot outrank its own later honest heads, but
40
+ its future-dated head is ignored until the clock catches up. Of two events with
41
+ the same created_at under one tag, the lower event ID is the newer (NIP-01).
42
+
43
+ Checkpoint plaintext shape is exported as `VaultCheckpoint`. It contains purpose,
44
+ rotation, sequence, revision, authorised device pubkeys and ordered chunk refs.
45
+ `nextRotation` is optional and, when present, exactly `rotation + 1`; any other
46
+ value makes the checkpoint invalid. Recovery begins at zero. A rotation
47
+ must not become canonical before the new copy is fetched, decrypted and verified.
48
+
49
+ Rotation is a revocation boundary. Recovery reads only the newest rotation:
50
+ the first rotation n whose successor n + 1 holds no authentic checkpoint-shaped
51
+ event. Before reading rotation n it always probes rotation n + 1 (one extra
52
+ resolve and query). Once n + 1 exists, rotation n is never queried, opened,
53
+ counted or merged, so a holder of a retired key can neither inject or tombstone
54
+ data (backdated or not, under any publisher tag) nor block recovery with
55
+ malformed, over-cap or junk-tag events, nor hide the successor by overwriting
56
+ the pointer head. The newest rotation is read strictly; a pointer there to a
57
+ rotation with no authentic checkpoint is damage (`unusable`). The rotating
58
+ writer must carry every head's state into the new rotation before publishing
59
+ there; nothing written under an older rotation is recovered. Sequence floors
60
+ apply to the rotation that is read.
61
+
62
+ A rotation revokes only a holder of THAT rotation's key, not the tree root: a
63
+ holder of the root key can derive every rotation regardless. A caller that
64
+ walks forward and finds every relay withholding the true newest rotation's
65
+ successor cannot tell that from a vault that never rotated further, so any
66
+ caller tracking the highest rotation it has already reached must treat a walk
67
+ that lands below it as a rollback, not as ready.
68
+
69
+ Rotation write order matters. The rotating writer first publishes the merged
70
+ state under rotation n + 1 (every chunk, then the checkpoint) and only then
71
+ declares `nextRotation` in rotation n. A pointer published first makes reads
72
+ `unusable` until rotation n + 1 is complete, because a pointer to a rotation
73
+ with no authentic checkpoint is damage. Once any authentic checkpoint-shaped
74
+ event exists under rotation n + 1, rotation n is never read: if that event (or
75
+ its chunks) is broken, the read is fail-closed `unusable` with no fallback to
76
+ rotation n. Recovery walks at most 32 rotations (`MAX_ROTATION_HOPS`); a vault
77
+ rotated more often reads `unusable`.
78
+
79
+ Restore results distinguish absent, unavailable, unusable and ready. Legacy may
80
+ be the canonical source only when the vault is absent, never merely because a
81
+ relay is offline or a new checkpoint fails to decrypt. A previously observed
82
+ sequence is a rollback floor. A fresh device cannot detect a relay withholding
83
+ all newer state without another trusted source; do not claim otherwise.
84
+
85
+ `createVaultRelayReader` queries at most eight `wss://` relays (plain `ws://`
86
+ only for `localhost` and `127.0.0.1`, matching `RelayClient`). A checkpoint
87
+ query, empty or not, completes only when every queried relay returned EOSE and
88
+ none failed: one relay's "nothing" is not evidence of absence, and one relay's
89
+ heads may omit a device head that another relay holds. Otherwise the query
90
+ throws a `VaultRelayError` whose `failedRelays` names the relays that failed,
91
+ and recovery returns `{ state: 'unavailable', failedRelays }`. A permanently
92
+ dead configured relay therefore keeps reads `unavailable` until it is removed
93
+ from the relay set; that is deliberate, since `unavailable` is safe and a false
94
+ `absent` is not. A chunk fetch by exact ID succeeds once any relay returns it
95
+ (its hash is pinned by the checkpoint); "not found" again needs every relay.
96
+ Each relay's events are re-checked against the requested author, kind, ID and
97
+ d-tag, checkpoint queries also drop events without a checkpoint-shaped d-tag,
98
+ and duplicates are removed, all before a per-relay cap of 128; a relay over the
99
+ cap counts as failed.
100
+
101
+ The SDK provides stateless read/validation and forward rotation traversal.
102
+ Writer scheduling, dataset import and durable canonical markers belong to
103
+ consumers. The candidate Signet app implements scheduling and confirmed-copy
104
+ state; this does not establish equivalent Sapwood behaviour.