@orangecheck/sdk 0.1.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -0
  3. package/dist/attestation.d.mts +44 -0
  4. package/dist/attestation.d.ts +44 -0
  5. package/dist/attestation.js +579 -0
  6. package/dist/attestation.js.map +1 -0
  7. package/dist/attestation.mjs +567 -0
  8. package/dist/attestation.mjs.map +1 -0
  9. package/dist/canonical.d.mts +15 -0
  10. package/dist/canonical.d.ts +15 -0
  11. package/dist/canonical.js +109 -0
  12. package/dist/canonical.js.map +1 -0
  13. package/dist/canonical.mjs +102 -0
  14. package/dist/canonical.mjs.map +1 -0
  15. package/dist/challenge.d.mts +40 -0
  16. package/dist/challenge.d.ts +40 -0
  17. package/dist/challenge.js +169 -0
  18. package/dist/challenge.js.map +1 -0
  19. package/dist/challenge.mjs +146 -0
  20. package/dist/challenge.mjs.map +1 -0
  21. package/dist/identity.d.mts +88 -0
  22. package/dist/identity.d.ts +88 -0
  23. package/dist/identity.js +730 -0
  24. package/dist/identity.js.map +1 -0
  25. package/dist/identity.mjs +702 -0
  26. package/dist/identity.mjs.map +1 -0
  27. package/dist/index.d.mts +47 -0
  28. package/dist/index.d.ts +47 -0
  29. package/dist/index.js +2004 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +1930 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/nostr.d.mts +19 -0
  34. package/dist/nostr.d.ts +19 -0
  35. package/dist/nostr.js +395 -0
  36. package/dist/nostr.js.map +1 -0
  37. package/dist/nostr.mjs +382 -0
  38. package/dist/nostr.mjs.map +1 -0
  39. package/dist/scoring.d.mts +16 -0
  40. package/dist/scoring.d.ts +16 -0
  41. package/dist/scoring.js +75 -0
  42. package/dist/scoring.js.map +1 -0
  43. package/dist/scoring.mjs +69 -0
  44. package/dist/scoring.mjs.map +1 -0
  45. package/dist/types-BJKDroFv.d.mts +62 -0
  46. package/dist/types-BJKDroFv.d.ts +62 -0
  47. package/dist/verify.d.mts +6 -0
  48. package/dist/verify.d.ts +6 -0
  49. package/dist/verify.js +509 -0
  50. package/dist/verify.js.map +1 -0
  51. package/dist/verify.mjs +485 -0
  52. package/dist/verify.mjs.map +1 -0
  53. package/package.json +106 -0
@@ -0,0 +1,730 @@
1
+ 'use strict';
2
+
3
+ // src/utils/logger.ts
4
+ function createLogger(namespace) {
5
+ const shouldLog = (level) => {
6
+ return false;
7
+ };
8
+ const formatMessage = (msg) => `[${namespace}] ${msg}`;
9
+ return {
10
+ debug: (contextOrMessage, message) => {
11
+ if (shouldLog()) {
12
+ if (typeof contextOrMessage === "string") {
13
+ console.debug(formatMessage(contextOrMessage));
14
+ } else {
15
+ console.debug(formatMessage(message ?? "Debug"), contextOrMessage);
16
+ }
17
+ }
18
+ },
19
+ info: (contextOrMessage, message) => {
20
+ if (shouldLog()) {
21
+ if (typeof contextOrMessage === "string") {
22
+ console.info(formatMessage(contextOrMessage));
23
+ } else {
24
+ console.info(formatMessage(message ?? "Info"), contextOrMessage);
25
+ }
26
+ }
27
+ },
28
+ warn: (contextOrMessage, message) => {
29
+ if (shouldLog()) {
30
+ if (typeof contextOrMessage === "string") {
31
+ console.warn(formatMessage(contextOrMessage));
32
+ } else {
33
+ console.warn(formatMessage(message ?? "Warning"), contextOrMessage);
34
+ }
35
+ }
36
+ },
37
+ error: (contextOrMessage, message) => {
38
+ if (shouldLog()) {
39
+ if (typeof contextOrMessage === "string") {
40
+ console.error(formatMessage(contextOrMessage));
41
+ } else {
42
+ console.error(formatMessage(message ?? "Error"), contextOrMessage);
43
+ }
44
+ }
45
+ }
46
+ };
47
+ }
48
+ function logVerificationError(protocol, identifier, error, context) {
49
+ const logger = createLogger("identity-verification");
50
+ logger.error(
51
+ {
52
+ protocol,
53
+ identifier,
54
+ error: error.message,
55
+ stack: error.stack,
56
+ ...context
57
+ },
58
+ "Verification failed"
59
+ );
60
+ }
61
+
62
+ // src/utils/rate-limiter.ts
63
+ var RateLimitError = class extends Error {
64
+ constructor(message = "Rate limit exceeded. Please try again later.") {
65
+ super(message);
66
+ this.name = "RateLimitError";
67
+ }
68
+ };
69
+ function createRateLimiter(maxRequests, windowMs) {
70
+ return {
71
+ maxRequests,
72
+ windowMs,
73
+ requests: /* @__PURE__ */ new Map()
74
+ };
75
+ }
76
+ function checkRateLimit(limiter, key) {
77
+ const now = Date.now();
78
+ const requests = limiter.requests.get(key) ?? [];
79
+ const validRequests = requests.filter((timestamp) => now - timestamp < limiter.windowMs);
80
+ if (validRequests.length >= limiter.maxRequests) {
81
+ return false;
82
+ }
83
+ validRequests.push(now);
84
+ limiter.requests.set(key, validRequests);
85
+ return true;
86
+ }
87
+ function enforceRateLimit(limiter, key) {
88
+ if (!checkRateLimit(limiter, key)) {
89
+ throw new RateLimitError();
90
+ }
91
+ }
92
+ var identityVerificationLimiter = createRateLimiter(5, 6e4);
93
+
94
+ // src/identity/dns.ts
95
+ var log = createLogger("identity-verification/dns");
96
+ async function verifyDnsIdentity(attestationId, domain) {
97
+ log.info({ attestationId, domain }, "Verifying DNS identity");
98
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
99
+ const wellKnownResult = await checkWellKnownFile(attestationId, cleanDomain);
100
+ if (wellKnownResult.verified) {
101
+ return wellKnownResult;
102
+ }
103
+ log.info({ domain: cleanDomain }, "Well-known file not found, DNS TXT check not implemented");
104
+ return {
105
+ verified: false,
106
+ error: "Verification file not found. Please create .well-known/orangecheck.txt"
107
+ };
108
+ }
109
+ async function checkWellKnownFile(attestationId, domain) {
110
+ const urls = [
111
+ `https://${domain}/.well-known/orangecheck.txt`,
112
+ `http://${domain}/.well-known/orangecheck.txt`
113
+ // Fallback to HTTP
114
+ ];
115
+ for (const url of urls) {
116
+ try {
117
+ log.info({ url }, "Checking well-known file");
118
+ const response = await fetch(url, {
119
+ method: "GET",
120
+ headers: {
121
+ Accept: "text/plain"
122
+ },
123
+ // Don't follow redirects to other domains
124
+ redirect: "manual"
125
+ });
126
+ if (response.ok) {
127
+ const content = await response.text();
128
+ if (content.includes(attestationId)) {
129
+ log.info({ domain, url }, "Found attestation ID in well-known file");
130
+ return {
131
+ verified: true,
132
+ method: "well-known",
133
+ url
134
+ };
135
+ } else {
136
+ log.info({ domain, url }, "Well-known file exists but no attestation ID found");
137
+ }
138
+ }
139
+ } catch (err) {
140
+ log.warn({ error: err, url }, "Failed to fetch well-known file");
141
+ }
142
+ }
143
+ return {
144
+ verified: false,
145
+ error: "Well-known file not found or does not contain attestation ID"
146
+ };
147
+ }
148
+ function getDnsVerificationInstructions(attestationId, domain) {
149
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
150
+ return `To verify your DNS domain (${cleanDomain}):
151
+
152
+ **Method 1: .well-known file (Recommended)**
153
+
154
+ 1. Create a file at: https://${cleanDomain}/.well-known/orangecheck.txt
155
+ 2. Add this content to the file:
156
+
157
+ OrangeCheck Attestation Verification
158
+ Domain: ${cleanDomain}
159
+ Attestation ID: ${attestationId}
160
+ Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
161
+
162
+ 3. Make sure the file is publicly accessible
163
+ 4. Return here and click "Verify"
164
+
165
+ **Method 2: DNS TXT record (Advanced)**
166
+
167
+ 1. Add a TXT record to your DNS:
168
+ Name: _orangecheck.${cleanDomain}
169
+ Value: ${attestationId}
170
+
171
+ 2. Wait for DNS propagation (up to 24 hours)
172
+ 3. Return here and click "Verify"
173
+
174
+ Note: Method 1 is faster and easier to set up.`;
175
+ }
176
+ function generateWellKnownContent(attestationId, domain) {
177
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
178
+ return `OrangeCheck Attestation Verification
179
+ =====================================
180
+
181
+ Domain: ${cleanDomain}
182
+ Attestation ID: ${attestationId}
183
+ Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
184
+
185
+ This file verifies that the owner of ${cleanDomain} controls the Bitcoin
186
+ address associated with attestation ${attestationId}.
187
+
188
+ Learn more: https://ochk.io
189
+
190
+ ---
191
+
192
+ Verification method: .well-known/orangecheck.txt
193
+ Protocol: OrangeCheck Protocol (OCP)
194
+ `;
195
+ }
196
+ function getWellKnownUrl(domain) {
197
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
198
+ return `https://${cleanDomain}/.well-known/orangecheck.txt`;
199
+ }
200
+ function isValidDomain(domain) {
201
+ const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
202
+ const domainPattern = /^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$/i;
203
+ return domainPattern.test(cleanDomain);
204
+ }
205
+ function isDnsIdentityVerified(attestationId, _domain, cachedContent) {
206
+ if (!cachedContent) {
207
+ return false;
208
+ }
209
+ return cachedContent.includes(attestationId);
210
+ }
211
+
212
+ // src/identity/github.ts
213
+ var log2 = createLogger("identity-verification/github");
214
+ async function verifyGitHubIdentity(attestationId, username) {
215
+ log2.info({ attestationId, username }, "Verifying GitHub identity");
216
+ try {
217
+ const response = await fetch(`https://api.github.com/users/${username}/gists`, {
218
+ headers: {
219
+ Accept: "application/vnd.github.v3+json"
220
+ }
221
+ });
222
+ if (!response.ok) {
223
+ if (response.status === 404) {
224
+ return {
225
+ verified: false,
226
+ error: `GitHub user '${username}' not found`
227
+ };
228
+ }
229
+ return {
230
+ verified: false,
231
+ error: `GitHub API error: ${response.status} ${response.statusText}`
232
+ };
233
+ }
234
+ const gists = await response.json();
235
+ log2.info({ username, gistCount: gists.length }, "Fetched GitHub gists");
236
+ for (const gist of gists) {
237
+ if (gist.description?.includes(attestationId)) {
238
+ log2.info({ username, gistId: gist.id }, "Found attestation ID in gist description");
239
+ return {
240
+ verified: true,
241
+ gistUrl: gist.html_url,
242
+ gistId: gist.id
243
+ };
244
+ }
245
+ for (const [filename, file] of Object.entries(gist.files)) {
246
+ if (file.content && file.content.includes(attestationId)) {
247
+ log2.info(
248
+ { username, gistId: gist.id, filename },
249
+ "Found attestation ID in gist file"
250
+ );
251
+ return {
252
+ verified: true,
253
+ gistUrl: gist.html_url,
254
+ gistId: gist.id
255
+ };
256
+ }
257
+ try {
258
+ const fileResponse = await fetch(file.raw_url);
259
+ if (fileResponse.ok) {
260
+ const content = await fileResponse.text();
261
+ if (content.includes(attestationId)) {
262
+ log2.info(
263
+ { username, gistId: gist.id, filename },
264
+ "Found attestation ID in gist file (fetched)"
265
+ );
266
+ return {
267
+ verified: true,
268
+ gistUrl: gist.html_url,
269
+ gistId: gist.id
270
+ };
271
+ }
272
+ }
273
+ } catch (err) {
274
+ log2.warn({ error: err, filename }, "Failed to fetch gist file content");
275
+ }
276
+ }
277
+ }
278
+ log2.info({ username }, "No verification gist found");
279
+ return {
280
+ verified: false,
281
+ error: "No gist found containing the attestation ID"
282
+ };
283
+ } catch (err) {
284
+ log2.error({ error: err, username }, "Failed to verify GitHub identity");
285
+ return {
286
+ verified: false,
287
+ error: err instanceof Error ? err.message : "Unknown error"
288
+ };
289
+ }
290
+ }
291
+ function getGitHubVerificationInstructions(attestationId) {
292
+ return `To verify your GitHub identity:
293
+
294
+ 1. Go to https://gist.github.com
295
+ 2. Create a new public gist
296
+ 3. Add a file with any name (e.g., "orangecheck.txt")
297
+ 4. Paste this text into the file:
298
+
299
+ OrangeCheck Attestation Verification
300
+ Attestation ID: ${attestationId}
301
+
302
+ 5. Click "Create public gist"
303
+ 6. Return here and click "Verify" to check
304
+
305
+ The verification will search your public gists for this attestation ID.`;
306
+ }
307
+ function generateGistTemplate(attestationId, username) {
308
+ return `# OrangeCheck Attestation Verification
309
+
310
+ I am verifying my GitHub identity (@${username}) for OrangeCheck.
311
+
312
+ **Attestation ID:** \`${attestationId}\`
313
+
314
+ **Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}
315
+
316
+ This gist proves that I control both:
317
+ - GitHub account: @${username}
318
+ - Bitcoin address associated with attestation ${attestationId}
319
+
320
+ ---
321
+
322
+ Learn more about OrangeCheck: https://ochk.io
323
+ `;
324
+ }
325
+ function isGitHubIdentityVerified(attestationId, _username, cachedGists) {
326
+ return cachedGists.some((gist) => {
327
+ if (gist.description?.includes(attestationId)) {
328
+ return true;
329
+ }
330
+ return Object.values(gist.files).some((file) => file.content?.includes(attestationId));
331
+ });
332
+ }
333
+ function getGistCreationUrl() {
334
+ return "https://gist.github.com/";
335
+ }
336
+ var DEFAULT_RELAYS = [
337
+ "wss://relay.damus.io",
338
+ "wss://relay.nostr.band",
339
+ "wss://nos.lol",
340
+ "wss://relay.snort.social"
341
+ ];
342
+
343
+ // src/identity/nostr.ts
344
+ var log4 = createLogger("identity-verification/nostr");
345
+ function npubToHex(npub) {
346
+ if (npub.match(/^[0-9a-f]{64}$/i)) {
347
+ return npub.toLowerCase();
348
+ }
349
+ log4.warn({ npub }, "npub to hex conversion not fully implemented");
350
+ return npub;
351
+ }
352
+ async function queryNostrEvents(options) {
353
+ const { authors, kinds, search, relays = DEFAULT_RELAYS, limit = 100 } = options;
354
+ const events = [];
355
+ log4.info({ authors, kinds, relayCount: relays.length }, "Querying Nostr for events");
356
+ const queryPromises = relays.map(async (relayUrl) => {
357
+ let ws = null;
358
+ try {
359
+ ws = new WebSocket(relayUrl);
360
+ const wsRef = ws;
361
+ await new Promise((resolve, reject) => {
362
+ const timeout = setTimeout(() => {
363
+ wsRef.close();
364
+ reject(new Error("Query timeout"));
365
+ }, 1e4);
366
+ const cleanup = () => {
367
+ clearTimeout(timeout);
368
+ wsRef.close();
369
+ };
370
+ wsRef.onopen = () => {
371
+ const subscriptionId = `verify_${Date.now()}`;
372
+ const filter = {
373
+ authors,
374
+ kinds,
375
+ limit
376
+ };
377
+ wsRef.send(JSON.stringify(["REQ", subscriptionId, filter]));
378
+ };
379
+ wsRef.onmessage = (msg) => {
380
+ try {
381
+ const data = JSON.parse(msg.data);
382
+ if (data[0] === "EVENT") {
383
+ const event = data[2];
384
+ if (search) {
385
+ if (event.content.includes(search)) {
386
+ events.push(event);
387
+ }
388
+ } else {
389
+ events.push(event);
390
+ }
391
+ } else if (data[0] === "EOSE") {
392
+ cleanup();
393
+ resolve();
394
+ }
395
+ } catch (err) {
396
+ log4.error({ relay: relayUrl, error: err }, "Failed to parse event");
397
+ }
398
+ };
399
+ wsRef.onerror = (err) => {
400
+ log4.error({ relay: relayUrl, error: err }, "WebSocket error during query");
401
+ cleanup();
402
+ reject(err);
403
+ };
404
+ wsRef.onclose = () => {
405
+ clearTimeout(timeout);
406
+ };
407
+ });
408
+ } catch (err) {
409
+ log4.error({ relay: relayUrl, error: err }, "Failed to query relay");
410
+ } finally {
411
+ if (ws && ws.readyState !== WebSocket.CLOSED) {
412
+ ws.close();
413
+ }
414
+ }
415
+ });
416
+ await Promise.allSettled(queryPromises);
417
+ log4.info({ eventCount: events.length }, "Query complete");
418
+ return events;
419
+ }
420
+ async function verifyNostrEventSignature(event) {
421
+ if (!event.sig || event.sig.length !== 128) {
422
+ return false;
423
+ }
424
+ log4.warn("Nostr signature verification not fully implemented");
425
+ return true;
426
+ }
427
+ async function verifyNostrIdentity(attestationId, npub, relays) {
428
+ log4.info({ attestationId, npub }, "Verifying Nostr identity");
429
+ try {
430
+ const pubkeyHex = npubToHex(npub);
431
+ const events = await queryNostrEvents({
432
+ authors: [pubkeyHex],
433
+ kinds: [1],
434
+ // Text notes
435
+ search: attestationId,
436
+ relays
437
+ });
438
+ if (events.length === 0) {
439
+ log4.info({ npub }, "No verification events found");
440
+ return {
441
+ verified: false,
442
+ error: "No verification event found. User must post a note containing the attestation ID."
443
+ };
444
+ }
445
+ for (const event of events) {
446
+ if (event.content.includes(attestationId)) {
447
+ const isValid = await verifyNostrEventSignature(event);
448
+ if (isValid) {
449
+ log4.info({ npub, eventId: event.id }, "Nostr identity verified");
450
+ return {
451
+ verified: true,
452
+ event
453
+ };
454
+ } else {
455
+ log4.warn({ npub, eventId: event.id }, "Invalid event signature");
456
+ }
457
+ }
458
+ }
459
+ return {
460
+ verified: false,
461
+ error: "Found events but none had valid signatures"
462
+ };
463
+ } catch (err) {
464
+ log4.error({ error: err, npub }, "Failed to verify Nostr identity");
465
+ return {
466
+ verified: false,
467
+ error: err instanceof Error ? err.message : "Unknown error"
468
+ };
469
+ }
470
+ }
471
+ function getNostrVerificationInstructions(attestationId) {
472
+ return `To verify your Nostr identity:
473
+
474
+ 1. Open your Nostr client (Damus, Amethyst, Primal, etc.)
475
+ 2. Post a note containing this text:
476
+
477
+ Verifying my OrangeCheck attestation: ${attestationId}
478
+
479
+ 3. Wait a few seconds for the note to propagate to relays
480
+ 4. Return here and click "Verify" to check
481
+
482
+ The verification will search Nostr relays for your note and confirm you control this identity.`;
483
+ }
484
+ function isNostrIdentityVerified(attestationId, npub, cachedEvents) {
485
+ const pubkeyHex = npubToHex(npub);
486
+ return cachedEvents.some(
487
+ (event) => event.pubkey === pubkeyHex && event.kind === 1 && event.content.includes(attestationId)
488
+ );
489
+ }
490
+
491
+ // src/identity/twitter.ts
492
+ var log5 = createLogger("identity-verification/twitter");
493
+ async function verifyTwitterIdentity(attestationId, handle, tweetUrl) {
494
+ log5.info({ attestationId, handle, tweetUrl }, "Verifying Twitter identity");
495
+ if (!tweetUrl) {
496
+ return {
497
+ verified: false,
498
+ error: "Tweet URL required for verification"
499
+ };
500
+ }
501
+ try {
502
+ const tweetId = extractTweetId(tweetUrl);
503
+ if (!tweetId) {
504
+ return {
505
+ verified: false,
506
+ error: "Invalid tweet URL format"
507
+ };
508
+ }
509
+ log5.info({ handle, tweetId }, "Manual verification required");
510
+ return {
511
+ verified: false,
512
+ tweetUrl,
513
+ tweetId,
514
+ error: "Automatic verification not available. Please verify manually by checking the tweet."
515
+ };
516
+ } catch (err) {
517
+ log5.error({ error: err, handle }, "Failed to verify Twitter identity");
518
+ return {
519
+ verified: false,
520
+ error: err instanceof Error ? err.message : "Unknown error"
521
+ };
522
+ }
523
+ }
524
+ function extractTweetId(url) {
525
+ const patterns = [
526
+ /(?:twitter\.com|x\.com)\/\w+\/status\/(\d+)/,
527
+ /(?:mobile\.twitter\.com)\/\w+\/status\/(\d+)/
528
+ ];
529
+ for (const pattern of patterns) {
530
+ const match = url.match(pattern);
531
+ if (match && match[1]) {
532
+ return match[1];
533
+ }
534
+ }
535
+ return null;
536
+ }
537
+ function getTwitterVerificationInstructions(attestationId, handle) {
538
+ const cleanHandle = handle.startsWith("@") ? handle.slice(1) : handle;
539
+ return `To verify your Twitter identity (@${cleanHandle}):
540
+
541
+ 1. Go to https://twitter.com/compose/tweet
542
+ 2. Post a tweet with this text:
543
+
544
+ Verifying my OrangeCheck attestation: ${attestationId}
545
+
546
+ 3. After posting, copy the tweet URL
547
+ 4. Paste the URL below and click "Verify"
548
+
549
+ Example tweet URL:
550
+ https://twitter.com/${cleanHandle}/status/1234567890
551
+
552
+ The verification will check that the tweet exists and contains the attestation ID.`;
553
+ }
554
+ function generateTweetText(attestationId) {
555
+ return `Verifying my OrangeCheck attestation: ${attestationId}
556
+
557
+ Proving Bitcoin reputation with cryptographic signatures.
558
+
559
+ #OrangeCheck #Bitcoin`;
560
+ }
561
+ function getTwitterComposeUrl(attestationId) {
562
+ const text = generateTweetText(attestationId);
563
+ const encoded = encodeURIComponent(text);
564
+ return `https://twitter.com/intent/tweet?text=${encoded}`;
565
+ }
566
+ function isValidTweetUrl(url) {
567
+ return extractTweetId(url) !== null;
568
+ }
569
+ function getTweetUrl(handle, tweetId) {
570
+ const cleanHandle = handle.startsWith("@") ? handle.slice(1) : handle;
571
+ return `https://twitter.com/${cleanHandle}/status/${tweetId}`;
572
+ }
573
+ function getManualVerificationInstructions(attestationId, handle, tweetUrl) {
574
+ return `Manual Verification Steps:
575
+
576
+ 1. Open this tweet: ${tweetUrl}
577
+ 2. Verify it's posted by @${handle}
578
+ 3. Check that the tweet contains: ${attestationId}
579
+ 4. Confirm the tweet is public and not deleted
580
+
581
+ If all checks pass, the identity is verified.`;
582
+ }
583
+
584
+ // src/identity/index.ts
585
+ async function verifyIdentity(attestationId, identity, options) {
586
+ const { protocol, identifier } = identity;
587
+ const rateLimitKey = `${protocol}:${identifier}`;
588
+ try {
589
+ enforceRateLimit(identityVerificationLimiter, rateLimitKey);
590
+ } catch (err) {
591
+ return {
592
+ verified: false,
593
+ error: err instanceof Error ? err.message : "Rate limit exceeded"
594
+ };
595
+ }
596
+ try {
597
+ switch (protocol) {
598
+ case "nostr":
599
+ return await verifyNostrIdentity(attestationId, identifier, options?.relays);
600
+ case "github":
601
+ return await verifyGitHubIdentity(attestationId, identifier);
602
+ case "twitter":
603
+ return await verifyTwitterIdentity(attestationId, identifier, options?.tweetUrl);
604
+ case "dns":
605
+ return await verifyDnsIdentity(attestationId, identifier);
606
+ case "email":
607
+ return {
608
+ verified: false,
609
+ error: "Email verification not implemented"
610
+ };
611
+ case "web":
612
+ return await verifyDnsIdentity(attestationId, identifier);
613
+ case "did":
614
+ return {
615
+ verified: false,
616
+ error: "DID verification not implemented"
617
+ };
618
+ default:
619
+ return {
620
+ verified: false,
621
+ error: `Unknown protocol: ${protocol}`
622
+ };
623
+ }
624
+ } catch (error) {
625
+ logVerificationError(
626
+ protocol,
627
+ identifier,
628
+ error instanceof Error ? error : new Error(String(error)),
629
+ {
630
+ attestationId
631
+ }
632
+ );
633
+ return {
634
+ verified: false,
635
+ error: error instanceof Error ? error.message : "Verification failed"
636
+ };
637
+ }
638
+ }
639
+ function getVerificationInstructions(attestationId, identity) {
640
+ const { protocol, identifier } = identity;
641
+ switch (protocol) {
642
+ case "nostr":
643
+ return getNostrVerificationInstructions(attestationId);
644
+ case "github":
645
+ return getGitHubVerificationInstructions(attestationId);
646
+ case "twitter":
647
+ return getTwitterVerificationInstructions(attestationId, identifier);
648
+ case "dns":
649
+ return getDnsVerificationInstructions(attestationId, identifier);
650
+ case "email":
651
+ return `Email verification not implemented yet.`;
652
+ case "web":
653
+ return getDnsVerificationInstructions(attestationId, identifier);
654
+ case "did":
655
+ return `DID verification not implemented yet.`;
656
+ default:
657
+ return `Unknown protocol: ${protocol}`;
658
+ }
659
+ }
660
+ function supportsAutomaticVerification(protocol) {
661
+ return ["nostr", "github", "dns", "web"].includes(protocol);
662
+ }
663
+ function requiresManualVerification(protocol) {
664
+ return ["twitter", "email"].includes(protocol);
665
+ }
666
+ async function verifyAllIdentities(attestationId, identities, options) {
667
+ const results = /* @__PURE__ */ new Map();
668
+ await Promise.all(
669
+ identities.map(async (identity) => {
670
+ const key = `${identity.protocol}:${identity.identifier}`;
671
+ const tweetUrl = options?.tweetUrls?.[identity.identifier];
672
+ const result = await verifyIdentity(attestationId, identity, {
673
+ tweetUrl,
674
+ relays: options?.relays
675
+ });
676
+ results.set(key, result);
677
+ })
678
+ );
679
+ return results;
680
+ }
681
+ function getVerificationSummary(results) {
682
+ let verified = 0;
683
+ let failed = 0;
684
+ let pending = 0;
685
+ for (const result of results.values()) {
686
+ if (result.verified) {
687
+ verified++;
688
+ } else if (result.error?.includes("not implemented")) {
689
+ pending++;
690
+ } else {
691
+ failed++;
692
+ }
693
+ }
694
+ return {
695
+ total: results.size,
696
+ verified,
697
+ failed,
698
+ pending
699
+ };
700
+ }
701
+
702
+ exports.generateGistTemplate = generateGistTemplate;
703
+ exports.generateTweetText = generateTweetText;
704
+ exports.generateWellKnownContent = generateWellKnownContent;
705
+ exports.getDnsVerificationInstructions = getDnsVerificationInstructions;
706
+ exports.getGistCreationUrl = getGistCreationUrl;
707
+ exports.getGitHubVerificationInstructions = getGitHubVerificationInstructions;
708
+ exports.getManualVerificationInstructions = getManualVerificationInstructions;
709
+ exports.getNostrVerificationInstructions = getNostrVerificationInstructions;
710
+ exports.getTweetUrl = getTweetUrl;
711
+ exports.getTwitterComposeUrl = getTwitterComposeUrl;
712
+ exports.getTwitterVerificationInstructions = getTwitterVerificationInstructions;
713
+ exports.getVerificationInstructions = getVerificationInstructions;
714
+ exports.getVerificationSummary = getVerificationSummary;
715
+ exports.getWellKnownUrl = getWellKnownUrl;
716
+ exports.isDnsIdentityVerified = isDnsIdentityVerified;
717
+ exports.isGitHubIdentityVerified = isGitHubIdentityVerified;
718
+ exports.isNostrIdentityVerified = isNostrIdentityVerified;
719
+ exports.isValidDomain = isValidDomain;
720
+ exports.isValidTweetUrl = isValidTweetUrl;
721
+ exports.requiresManualVerification = requiresManualVerification;
722
+ exports.supportsAutomaticVerification = supportsAutomaticVerification;
723
+ exports.verifyAllIdentities = verifyAllIdentities;
724
+ exports.verifyDnsIdentity = verifyDnsIdentity;
725
+ exports.verifyGitHubIdentity = verifyGitHubIdentity;
726
+ exports.verifyIdentity = verifyIdentity;
727
+ exports.verifyNostrIdentity = verifyNostrIdentity;
728
+ exports.verifyTwitterIdentity = verifyTwitterIdentity;
729
+ //# sourceMappingURL=identity.js.map
730
+ //# sourceMappingURL=identity.js.map