@keytrace/runner 0.0.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.
Files changed (142) hide show
  1. package/README.md +139 -0
  2. package/dist/actions/css-select.d.ts +6 -0
  3. package/dist/actions/css-select.d.ts.map +1 -0
  4. package/dist/actions/css-select.js +14 -0
  5. package/dist/actions/css-select.js.map +1 -0
  6. package/dist/actions/dns-txt.d.ts +6 -0
  7. package/dist/actions/dns-txt.d.ts.map +1 -0
  8. package/dist/actions/dns-txt.js +17 -0
  9. package/dist/actions/dns-txt.js.map +1 -0
  10. package/dist/actions/http-get.d.ts +6 -0
  11. package/dist/actions/http-get.d.ts.map +1 -0
  12. package/dist/actions/http-get.js +19 -0
  13. package/dist/actions/http-get.js.map +1 -0
  14. package/dist/actions/index.d.ts +6 -0
  15. package/dist/actions/index.d.ts.map +1 -0
  16. package/dist/actions/index.js +6 -0
  17. package/dist/actions/index.js.map +1 -0
  18. package/dist/actions/json-path.d.ts +12 -0
  19. package/dist/actions/json-path.d.ts.map +1 -0
  20. package/dist/actions/json-path.js +26 -0
  21. package/dist/actions/json-path.js.map +1 -0
  22. package/dist/actions/regex-match.d.ts +6 -0
  23. package/dist/actions/regex-match.d.ts.map +1 -0
  24. package/dist/actions/regex-match.js +14 -0
  25. package/dist/actions/regex-match.js.map +1 -0
  26. package/dist/claim.d.ts +38 -0
  27. package/dist/claim.d.ts.map +1 -0
  28. package/dist/claim.js +253 -0
  29. package/dist/claim.js.map +1 -0
  30. package/dist/constants.d.ts +17 -0
  31. package/dist/constants.d.ts.map +1 -0
  32. package/dist/constants.js +17 -0
  33. package/dist/constants.js.map +1 -0
  34. package/dist/expect.d.ts +12 -0
  35. package/dist/expect.d.ts.map +1 -0
  36. package/dist/expect.js +33 -0
  37. package/dist/expect.js.map +1 -0
  38. package/dist/fetchers/activitypub.d.ts +25 -0
  39. package/dist/fetchers/activitypub.d.ts.map +1 -0
  40. package/dist/fetchers/activitypub.js +32 -0
  41. package/dist/fetchers/activitypub.js.map +1 -0
  42. package/dist/fetchers/dns.d.ts +21 -0
  43. package/dist/fetchers/dns.d.ts.map +1 -0
  44. package/dist/fetchers/dns.js +61 -0
  45. package/dist/fetchers/dns.js.map +1 -0
  46. package/dist/fetchers/http.d.ts +10 -0
  47. package/dist/fetchers/http.d.ts.map +1 -0
  48. package/dist/fetchers/http.js +30 -0
  49. package/dist/fetchers/http.js.map +1 -0
  50. package/dist/fetchers/index.d.ts +16 -0
  51. package/dist/fetchers/index.d.ts.map +1 -0
  52. package/dist/fetchers/index.js +22 -0
  53. package/dist/fetchers/index.js.map +1 -0
  54. package/dist/index.d.ts +21 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +26 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/interpolate.d.ts +12 -0
  59. package/dist/interpolate.d.ts.map +1 -0
  60. package/dist/interpolate.js +23 -0
  61. package/dist/interpolate.js.map +1 -0
  62. package/dist/profile.d.ts +42 -0
  63. package/dist/profile.d.ts.map +1 -0
  64. package/dist/profile.js +176 -0
  65. package/dist/profile.js.map +1 -0
  66. package/dist/recipes/dns-txt.d.ts +9 -0
  67. package/dist/recipes/dns-txt.d.ts.map +1 -0
  68. package/dist/recipes/dns-txt.js +45 -0
  69. package/dist/recipes/dns-txt.js.map +1 -0
  70. package/dist/recipes/github-gist.d.ts +9 -0
  71. package/dist/recipes/github-gist.d.ts.map +1 -0
  72. package/dist/recipes/github-gist.js +52 -0
  73. package/dist/recipes/github-gist.js.map +1 -0
  74. package/dist/recipes/index.d.ts +3 -0
  75. package/dist/recipes/index.d.ts.map +1 -0
  76. package/dist/recipes/index.js +3 -0
  77. package/dist/recipes/index.js.map +1 -0
  78. package/dist/runner.d.ts +7 -0
  79. package/dist/runner.d.ts.map +1 -0
  80. package/dist/runner.js +100 -0
  81. package/dist/runner.js.map +1 -0
  82. package/dist/serviceProviders/activitypub.d.ts +10 -0
  83. package/dist/serviceProviders/activitypub.d.ts.map +1 -0
  84. package/dist/serviceProviders/activitypub.js +73 -0
  85. package/dist/serviceProviders/activitypub.js.map +1 -0
  86. package/dist/serviceProviders/bsky.d.ts +10 -0
  87. package/dist/serviceProviders/bsky.d.ts.map +1 -0
  88. package/dist/serviceProviders/bsky.js +63 -0
  89. package/dist/serviceProviders/bsky.js.map +1 -0
  90. package/dist/serviceProviders/dns.d.ts +10 -0
  91. package/dist/serviceProviders/dns.d.ts.map +1 -0
  92. package/dist/serviceProviders/dns.js +65 -0
  93. package/dist/serviceProviders/dns.js.map +1 -0
  94. package/dist/serviceProviders/github.d.ts +10 -0
  95. package/dist/serviceProviders/github.d.ts.map +1 -0
  96. package/dist/serviceProviders/github.js +100 -0
  97. package/dist/serviceProviders/github.js.map +1 -0
  98. package/dist/serviceProviders/index.d.ts +26 -0
  99. package/dist/serviceProviders/index.d.ts.map +1 -0
  100. package/dist/serviceProviders/index.js +55 -0
  101. package/dist/serviceProviders/index.js.map +1 -0
  102. package/dist/serviceProviders/npm.d.ts +10 -0
  103. package/dist/serviceProviders/npm.d.ts.map +1 -0
  104. package/dist/serviceProviders/npm.js +99 -0
  105. package/dist/serviceProviders/npm.js.map +1 -0
  106. package/dist/serviceProviders/types.d.ts +106 -0
  107. package/dist/serviceProviders/types.d.ts.map +1 -0
  108. package/dist/serviceProviders/types.js +2 -0
  109. package/dist/serviceProviders/types.js.map +1 -0
  110. package/dist/types.d.ts +165 -0
  111. package/dist/types.d.ts.map +1 -0
  112. package/dist/types.js +12 -0
  113. package/dist/types.js.map +1 -0
  114. package/package.json +37 -0
  115. package/src/actions/css-select.ts +14 -0
  116. package/src/actions/dns-txt.ts +16 -0
  117. package/src/actions/http-get.ts +19 -0
  118. package/src/actions/index.ts +5 -0
  119. package/src/actions/json-path.ts +29 -0
  120. package/src/actions/regex-match.ts +13 -0
  121. package/src/claim.ts +293 -0
  122. package/src/constants.ts +19 -0
  123. package/src/expect.ts +36 -0
  124. package/src/fetchers/activitypub.ts +53 -0
  125. package/src/fetchers/dns.ts +82 -0
  126. package/src/fetchers/http.ts +38 -0
  127. package/src/fetchers/index.ts +30 -0
  128. package/src/index.ts +57 -0
  129. package/src/interpolate.ts +20 -0
  130. package/src/profile.ts +229 -0
  131. package/src/recipes/dns-txt.ts +46 -0
  132. package/src/recipes/github-gist.ts +53 -0
  133. package/src/recipes/index.ts +2 -0
  134. package/src/runner.ts +116 -0
  135. package/src/serviceProviders/activitypub.ts +84 -0
  136. package/src/serviceProviders/bsky.ts +73 -0
  137. package/src/serviceProviders/dns.ts +75 -0
  138. package/src/serviceProviders/github.ts +112 -0
  139. package/src/serviceProviders/index.ts +65 -0
  140. package/src/serviceProviders/npm.ts +116 -0
  141. package/src/serviceProviders/types.ts +121 -0
  142. package/src/types.ts +181 -0
@@ -0,0 +1,176 @@
1
+ import { AtpAgent } from "@atproto/api";
2
+ import { createClaim, verifyClaim } from "./claim.js";
3
+ import { ClaimStatus } from "./types.js";
4
+ import { COLLECTION_NSID, PUBLIC_API_URL, PLC_DIRECTORY_URL } from "./constants.js";
5
+ /**
6
+ * Resolve the PDS endpoint from a DID document.
7
+ * For did:plc, fetches from plc.directory.
8
+ * For did:web, fetches from the well-known DID path.
9
+ * Falls back to PUBLIC_API_URL on failure.
10
+ */
11
+ export async function resolvePds(did) {
12
+ try {
13
+ let url;
14
+ if (did.startsWith("did:plc:")) {
15
+ url = `${PLC_DIRECTORY_URL}/${did}`;
16
+ }
17
+ else if (did.startsWith("did:web:")) {
18
+ const host = did.replace("did:web:", "").replaceAll(":", "/");
19
+ url = `https://${host}/.well-known/did.json`;
20
+ }
21
+ else {
22
+ return PUBLIC_API_URL;
23
+ }
24
+ const response = await globalThis.fetch(url, {
25
+ headers: { Accept: "application/json" },
26
+ });
27
+ if (!response.ok) {
28
+ return PUBLIC_API_URL;
29
+ }
30
+ const doc = (await response.json());
31
+ const pdsService = doc.service?.find((s) => s.id === "#atproto_pds" || s.type === "AtprotoPersonalDataServer");
32
+ return pdsService?.serviceEndpoint ?? PUBLIC_API_URL;
33
+ }
34
+ catch {
35
+ return PUBLIC_API_URL;
36
+ }
37
+ }
38
+ /**
39
+ * Parse an AT URI and extract the rkey (record key).
40
+ * AT URIs have the format: at://did/collection/rkey
41
+ */
42
+ function parseAtUriRkey(atUri) {
43
+ const match = atUri.match(/^at:\/\/[^/]+\/[^/]+\/(.+)$/);
44
+ return match?.[1] ?? "";
45
+ }
46
+ /**
47
+ * Internal: fetch profile data using an already-configured agent
48
+ */
49
+ async function fetchWithAgent(agent, did) {
50
+ // Fetch Bluesky profile for display info via public API (not PDS)
51
+ // The PDS doesn't serve app.bsky.actor.getProfile - only the AppView does
52
+ let bskyProfile = null;
53
+ try {
54
+ const publicAgent = new AtpAgent({ service: PUBLIC_API_URL });
55
+ const profileRes = await publicAgent.getProfile({ actor: did });
56
+ bskyProfile = {
57
+ handle: profileRes.data.handle,
58
+ displayName: profileRes.data.displayName,
59
+ avatar: profileRes.data.avatar,
60
+ };
61
+ }
62
+ catch (err) {
63
+ // Profile fetch is optional - user may not have a Bluesky profile
64
+ // 404 is expected; log other errors at debug level
65
+ if (err instanceof Error && !err.message.includes("404")) {
66
+ console.debug(`Failed to fetch profile for ${did}: ${err.message}`);
67
+ }
68
+ }
69
+ // List all claim records with cursor-based pagination
70
+ const claims = [];
71
+ try {
72
+ let cursor;
73
+ do {
74
+ const records = await agent.com.atproto.repo.listRecords({
75
+ repo: did,
76
+ collection: COLLECTION_NSID,
77
+ limit: 100,
78
+ cursor,
79
+ });
80
+ for (const record of records.data.records) {
81
+ const value = record.value;
82
+ if (value.claimUri) {
83
+ claims.push({
84
+ uri: value.claimUri,
85
+ did,
86
+ type: value.type,
87
+ comment: value.comment,
88
+ createdAt: value.createdAt ?? new Date().toISOString(),
89
+ rkey: parseAtUriRkey(record.uri),
90
+ identity: value.identity,
91
+ });
92
+ }
93
+ }
94
+ cursor = records.data.cursor;
95
+ } while (cursor);
96
+ }
97
+ catch (err) {
98
+ // 404 means no records yet; log other errors
99
+ if (err instanceof Error && !err.message.includes("404")) {
100
+ console.debug(`Failed to list claim records for ${did}: ${err.message}`);
101
+ }
102
+ }
103
+ return {
104
+ did,
105
+ handle: bskyProfile?.handle ?? did,
106
+ displayName: bskyProfile?.displayName,
107
+ avatar: bskyProfile?.avatar,
108
+ claims,
109
+ claimInstances: claims.map((c) => createClaim(c.uri, did)),
110
+ };
111
+ }
112
+ /**
113
+ * Fetch a profile from ATProto by DID or handle
114
+ */
115
+ export async function fetchProfile(didOrHandle, serviceUrl) {
116
+ // Resolve PDS from DID document unless an explicit serviceUrl was provided
117
+ let resolvedServiceUrl;
118
+ let did = didOrHandle;
119
+ if (serviceUrl) {
120
+ resolvedServiceUrl = serviceUrl;
121
+ }
122
+ else if (didOrHandle.startsWith("did:")) {
123
+ resolvedServiceUrl = await resolvePds(didOrHandle);
124
+ }
125
+ else {
126
+ // Handle - we need to resolve via the public API first, then resolve PDS
127
+ resolvedServiceUrl = PUBLIC_API_URL;
128
+ }
129
+ const agent = new AtpAgent({ service: resolvedServiceUrl });
130
+ // Resolve handle to DID if needed
131
+ if (!didOrHandle.startsWith("did:")) {
132
+ const resolved = await agent.resolveHandle({ handle: didOrHandle });
133
+ did = resolved.data.did;
134
+ // Now that we have the DID, resolve the actual PDS if no explicit serviceUrl
135
+ if (!serviceUrl) {
136
+ const pdsUrl = await resolvePds(did);
137
+ if (pdsUrl !== resolvedServiceUrl) {
138
+ resolvedServiceUrl = pdsUrl;
139
+ // Re-create agent pointed at the user's actual PDS
140
+ const pdsAgent = new AtpAgent({ service: pdsUrl });
141
+ return fetchWithAgent(pdsAgent, did);
142
+ }
143
+ }
144
+ }
145
+ return fetchWithAgent(agent, did);
146
+ }
147
+ /**
148
+ * Verify all claims in a profile
149
+ */
150
+ export async function verifyAllClaims(profile, opts) {
151
+ await Promise.all(profile.claimInstances.map((claim) => verifyClaim(claim, opts)));
152
+ }
153
+ /**
154
+ * Get verification summary for a profile
155
+ */
156
+ export function getProfileSummary(profile) {
157
+ const claims = profile.claimInstances;
158
+ return {
159
+ total: claims.length,
160
+ verified: claims.filter((c) => c.status === ClaimStatus.VERIFIED).length,
161
+ failed: claims.filter((c) => c.status === ClaimStatus.FAILED || c.status === ClaimStatus.ERROR).length,
162
+ pending: claims.filter((c) => c.status === ClaimStatus.INIT || c.status === ClaimStatus.MATCHED).length,
163
+ };
164
+ }
165
+ /**
166
+ * Get claims grouped by status
167
+ */
168
+ export function getClaimsByStatus(profile) {
169
+ const claims = profile.claimInstances;
170
+ return {
171
+ verified: claims.filter((c) => c.status === ClaimStatus.VERIFIED),
172
+ failed: claims.filter((c) => c.status === ClaimStatus.FAILED || c.status === ClaimStatus.ERROR),
173
+ pending: claims.filter((c) => c.status === ClaimStatus.INIT || c.status === ClaimStatus.MATCHED),
174
+ };
175
+ }
176
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Recipe } from "../types.js";
2
+ /**
3
+ * Built-in recipe: Domain verification via DNS TXT record.
4
+ *
5
+ * The user adds a TXT record to their domain containing their DID,
6
+ * then provides the domain name for verification.
7
+ */
8
+ export declare const dnsTxtRecipe: Recipe;
9
+ //# sourceMappingURL=dns-txt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-txt.d.ts","sourceRoot":"","sources":["../../src/recipes/dns-txt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,MAqC1B,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Built-in recipe: Domain verification via DNS TXT record.
3
+ *
4
+ * The user adds a TXT record to their domain containing their DID,
5
+ * then provides the domain name for verification.
6
+ */
7
+ export const dnsTxtRecipe = {
8
+ $type: "dev.keytrace.recipe",
9
+ type: "dns",
10
+ version: 1,
11
+ displayName: "Domain (via DNS TXT)",
12
+ params: [
13
+ {
14
+ key: "domain",
15
+ label: "Domain name",
16
+ type: "domain",
17
+ placeholder: "example.com",
18
+ pattern: "^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)+$",
19
+ },
20
+ ],
21
+ instructions: {
22
+ steps: [
23
+ "Log into your domain's DNS management panel",
24
+ "Add a new TXT record to the root domain (or _keytrace subdomain)",
25
+ "Set the value to the verification text below",
26
+ "Wait for DNS propagation (may take a few minutes)",
27
+ ],
28
+ proofTemplate: "keytrace-verification={did}",
29
+ proofLocation: "DNS TXT record on your domain",
30
+ },
31
+ verification: {
32
+ steps: [
33
+ {
34
+ action: "dns-txt",
35
+ url: "{domain}",
36
+ },
37
+ {
38
+ action: "regex-match",
39
+ pattern: "keytrace-verification=({did})",
40
+ expect: "equals:{did}",
41
+ },
42
+ ],
43
+ },
44
+ };
45
+ //# sourceMappingURL=dns-txt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-txt.js","sourceRoot":"","sources":["../../src/recipes/dns-txt.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAW;IAClC,KAAK,EAAE,qBAAqB;IAC5B,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,sBAAsB;IACnC,MAAM,EAAE;QACN;YACE,GAAG,EAAE,QAAQ;YACb,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,aAAa;YAC1B,OAAO,EAAE,4FAA4F;SACtG;KACF;IACD,YAAY,EAAE;QACZ,KAAK,EAAE;YACL,6CAA6C;YAC7C,kEAAkE;YAClE,8CAA8C;YAC9C,mDAAmD;SACpD;QACD,aAAa,EAAE,6BAA6B;QAC5C,aAAa,EAAE,+BAA+B;KAC/C;IACD,YAAY,EAAE;QACZ,KAAK,EAAE;YACL;gBACE,MAAM,EAAE,SAAS;gBACjB,GAAG,EAAE,UAAU;aAChB;YACD;gBACE,MAAM,EAAE,aAAa;gBACrB,OAAO,EAAE,+BAA+B;gBACxC,MAAM,EAAE,cAAc;aACvB;SACF;KACF;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Recipe } from "../types.js";
2
+ /**
3
+ * Built-in recipe: GitHub Account verification via public Gist.
4
+ *
5
+ * The user creates a public gist named keytrace.json containing
6
+ * their claim ID and DID, then provides the gist URL for verification.
7
+ */
8
+ export declare const githubGistRecipe: Recipe;
9
+ //# sourceMappingURL=github-gist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-gist.d.ts","sourceRoot":"","sources":["../../src/recipes/github-gist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MA4C9B,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Built-in recipe: GitHub Account verification via public Gist.
3
+ *
4
+ * The user creates a public gist named keytrace.json containing
5
+ * their claim ID and DID, then provides the gist URL for verification.
6
+ */
7
+ export const githubGistRecipe = {
8
+ $type: "dev.keytrace.recipe",
9
+ type: "github-gist",
10
+ version: 1,
11
+ displayName: "GitHub Account (via Gist)",
12
+ params: [
13
+ {
14
+ key: "gistUrl",
15
+ label: "Gist URL",
16
+ type: "url",
17
+ placeholder: "https://gist.github.com/octocat/abc123...",
18
+ pattern: "^https://gist\\.github\\.com/([^/]+)/([a-f0-9]+)$",
19
+ extractFrom: "^https://gist\\.github\\.com/([^/]+)/",
20
+ },
21
+ ],
22
+ instructions: {
23
+ steps: [
24
+ "Go to https://gist.github.com",
25
+ "Create a new public gist",
26
+ "Name the file `keytrace.json`",
27
+ "Paste the verification content below into the file",
28
+ "Save the gist and paste the URL below",
29
+ ],
30
+ proofTemplate: '{\n "keytrace": "{claimId}",\n "did": "{did}"\n}',
31
+ proofLocation: "Public gist with keytrace.json",
32
+ },
33
+ verification: {
34
+ steps: [
35
+ {
36
+ action: "http-get",
37
+ url: "{gistUrl}/raw/keytrace.json",
38
+ },
39
+ {
40
+ action: "json-path",
41
+ selector: "$.keytrace",
42
+ expect: "equals:{claimId}",
43
+ },
44
+ {
45
+ action: "json-path",
46
+ selector: "$.did",
47
+ expect: "equals:{did}",
48
+ },
49
+ ],
50
+ },
51
+ };
52
+ //# sourceMappingURL=github-gist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-gist.js","sourceRoot":"","sources":["../../src/recipes/github-gist.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAW;IACtC,KAAK,EAAE,qBAAqB;IAC5B,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,2BAA2B;IACxC,MAAM,EAAE;QACN;YACE,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,KAAK;YACX,WAAW,EAAE,2CAA2C;YACxD,OAAO,EAAE,mDAAmD;YAC5D,WAAW,EAAE,uCAAuC;SACrD;KACF;IACD,YAAY,EAAE;QACZ,KAAK,EAAE;YACL,+BAA+B;YAC/B,0BAA0B;YAC1B,+BAA+B;YAC/B,oDAAoD;YACpD,uCAAuC;SACxC;QACD,aAAa,EAAE,oDAAoD;QACnE,aAAa,EAAE,gCAAgC;KAChD;IACD,YAAY,EAAE;QACZ,KAAK,EAAE;YACL;gBACE,MAAM,EAAE,UAAU;gBAClB,GAAG,EAAE,6BAA6B;aACnC;YACD;gBACE,MAAM,EAAE,WAAW;gBACnB,QAAQ,EAAE,YAAY;gBACtB,MAAM,EAAE,kBAAkB;aAC3B;YACD;gBACE,MAAM,EAAE,WAAW;gBACnB,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,cAAc;aACvB;SACF;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { githubGistRecipe } from "./github-gist.js";
2
+ export { dnsTxtRecipe } from "./dns-txt.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recipes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { githubGistRecipe } from "./github-gist.js";
2
+ export { dnsTxtRecipe } from "./dns-txt.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/recipes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Recipe, ClaimContext, RunnerConfig, VerificationResult } from "./types.js";
2
+ /**
3
+ * Execute a recipe's verification steps against a claim context.
4
+ * Returns a full result with per-step details. Stops on first failure.
5
+ */
6
+ export declare function runRecipe(recipe: Recipe, context: ClaimContext, config?: RunnerConfig): Promise<VerificationResult>;
7
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAyC,MAAM,YAAY,CAAC;AAWhI;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuCzH"}
package/dist/runner.js ADDED
@@ -0,0 +1,100 @@
1
+ import { interpolate } from "./interpolate.js";
2
+ import { checkExpect } from "./expect.js";
3
+ import { httpGet } from "./actions/http-get.js";
4
+ import { jsonPath } from "./actions/json-path.js";
5
+ import { cssSelect } from "./actions/css-select.js";
6
+ import { regexMatch } from "./actions/regex-match.js";
7
+ import { dnsTxt } from "./actions/dns-txt.js";
8
+ const DEFAULT_TIMEOUT = 10_000;
9
+ /**
10
+ * Execute a recipe's verification steps against a claim context.
11
+ * Returns a full result with per-step details. Stops on first failure.
12
+ */
13
+ export async function runRecipe(recipe, context, config) {
14
+ const fetchFn = config?.fetch ?? globalThis.fetch;
15
+ const timeout = config?.timeout ?? DEFAULT_TIMEOUT;
16
+ const steps = [];
17
+ // Extract subject from params if a param defines extractFrom
18
+ let subject;
19
+ if (recipe.params) {
20
+ for (const param of recipe.params) {
21
+ if (param.extractFrom && context.params[param.key]) {
22
+ const regex = new RegExp(param.extractFrom);
23
+ const match = context.params[param.key].match(regex);
24
+ if (match?.[1]) {
25
+ subject = `${recipe.type.split("-")[0]}:${match[1]}`;
26
+ }
27
+ }
28
+ }
29
+ }
30
+ // Tracks the last "fetch" output (http-get, dns-txt) for extraction steps to use.
31
+ // Extraction steps (json-path, css-select, regex-match) always operate on the
32
+ // last fetch output, not on each other's output.
33
+ let lastFetchOutput = undefined;
34
+ for (const step of recipe.verification.steps) {
35
+ const isFetchAction = step.action === "http-get" || step.action === "dns-txt";
36
+ const result = await executeStep(step, context, lastFetchOutput, fetchFn, timeout);
37
+ steps.push(result);
38
+ if (!result.success) {
39
+ return { success: false, steps, subject, error: result.error };
40
+ }
41
+ if (isFetchAction) {
42
+ lastFetchOutput = result.data;
43
+ }
44
+ }
45
+ return { success: true, steps, subject };
46
+ }
47
+ async function executeStep(step, context, previousOutput, fetchFn, timeout) {
48
+ try {
49
+ let data;
50
+ switch (step.action) {
51
+ case "http-get": {
52
+ const url = interpolate(step.url, context);
53
+ data = await httpGet(url, fetchFn, timeout);
54
+ break;
55
+ }
56
+ case "json-path": {
57
+ const selector = interpolate(step.selector, context);
58
+ const input = previousOutput ?? "";
59
+ data = jsonPath(input, selector);
60
+ break;
61
+ }
62
+ case "css-select": {
63
+ const selector = interpolate(step.selector, context);
64
+ const input = previousOutput;
65
+ if (typeof input !== "string") {
66
+ throw new Error("css-select requires string input from a previous step");
67
+ }
68
+ data = cssSelect(input, selector);
69
+ break;
70
+ }
71
+ case "regex-match": {
72
+ const pattern = interpolate(step.pattern, context);
73
+ const input = typeof previousOutput === "string" ? previousOutput : String(previousOutput ?? "");
74
+ data = regexMatch(input, pattern);
75
+ break;
76
+ }
77
+ case "dns-txt": {
78
+ const domain = step.url ? interpolate(step.url, context) : interpolate(step.pattern, context);
79
+ data = await dnsTxt(domain);
80
+ break;
81
+ }
82
+ default:
83
+ throw new Error(`Unknown action: "${step.action}"`);
84
+ }
85
+ // Check expect if defined
86
+ if (step.expect) {
87
+ const expectStr = interpolate(step.expect, context);
88
+ const result = checkExpect(expectStr, data);
89
+ if (!result.pass) {
90
+ return { action: step.action, success: false, data, error: result.message };
91
+ }
92
+ }
93
+ return { action: step.action, success: true, data };
94
+ }
95
+ catch (err) {
96
+ const message = err instanceof Error ? err.message : String(err);
97
+ return { action: step.action, success: false, error: message };
98
+ }
99
+ }
100
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ServiceProvider } from "./types.js";
2
+ /**
3
+ * ActivityPub (Mastodon/Fediverse) service provider
4
+ *
5
+ * Users prove ownership by adding their DID to their profile bio or fields.
6
+ * The claim URI is the profile URL (e.g., https://mastodon.social/@username)
7
+ */
8
+ declare const activitypub: ServiceProvider;
9
+ export default activitypub;
10
+ //# sourceMappingURL=activitypub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activitypub.d.ts","sourceRoot":"","sources":["../../src/serviceProviders/activitypub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,QAAA,MAAM,WAAW,EAAE,eAyElB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * ActivityPub (Mastodon/Fediverse) service provider
3
+ *
4
+ * Users prove ownership by adding their DID to their profile bio or fields.
5
+ * The claim URI is the profile URL (e.g., https://mastodon.social/@username)
6
+ */
7
+ const activitypub = {
8
+ id: "activitypub",
9
+ name: "Mastodon",
10
+ homepage: "https://joinmastodon.org",
11
+ // Match Mastodon-style profile URLs: https://instance/@username
12
+ reUri: /^https:\/\/([^/]+)\/@([^/]+)\/?$/,
13
+ // Could match other ActivityPub software with same URL pattern
14
+ isAmbiguous: true,
15
+ ui: {
16
+ description: "Link your Mastodon or Fediverse account",
17
+ icon: "at-sign",
18
+ inputLabel: "Profile URL",
19
+ inputPlaceholder: "https://mastodon.social/@username",
20
+ instructions: [
21
+ "Go to your Mastodon instance and open **Edit profile**",
22
+ "Add your DID to your **bio** or create a new **profile metadata field**",
23
+ "For metadata fields, set the label to `keytrace` and paste your DID as the value",
24
+ "Save your profile changes",
25
+ "Paste your full profile URL below (e.g., `https://mastodon.social/@username`)",
26
+ ],
27
+ proofTemplate: "{did}",
28
+ },
29
+ processURI(uri, match) {
30
+ const [, domain, username] = match;
31
+ return {
32
+ profile: {
33
+ display: `@${username}@${domain}`,
34
+ uri,
35
+ },
36
+ proof: {
37
+ request: {
38
+ uri,
39
+ fetcher: "activitypub",
40
+ format: "json",
41
+ },
42
+ target: [
43
+ // Check profile bio/summary (HTML content)
44
+ { path: ["summary"], relation: "contains", format: "text" },
45
+ // Check profile fields (Mastodon-style verification fields)
46
+ { path: ["attachment", "*", "value"], relation: "contains", format: "text" },
47
+ ],
48
+ },
49
+ };
50
+ },
51
+ postprocess(data) {
52
+ const actor = data;
53
+ return {
54
+ displayName: actor.name || actor.preferredUsername,
55
+ avatarUrl: actor.icon?.url,
56
+ };
57
+ },
58
+ getProofText(did) {
59
+ return did;
60
+ },
61
+ getProofLocation() {
62
+ return `Add to your profile bio or a profile metadata field`;
63
+ },
64
+ tests: [
65
+ { uri: "https://mastodon.social/@alice", shouldMatch: true },
66
+ { uri: "https://fosstodon.org/@bob/", shouldMatch: true },
67
+ { uri: "https://hachyderm.io/@user", shouldMatch: true },
68
+ { uri: "https://twitter.com/alice", shouldMatch: false },
69
+ { uri: "https://mastodon.social/alice", shouldMatch: false },
70
+ ],
71
+ };
72
+ export default activitypub;
73
+ //# sourceMappingURL=activitypub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activitypub.js","sourceRoot":"","sources":["../../src/serviceProviders/activitypub.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,GAAoB;IACnC,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,0BAA0B;IAEpC,gEAAgE;IAChE,KAAK,EAAE,kCAAkC;IAEzC,+DAA+D;IAC/D,WAAW,EAAE,IAAI;IAEjB,EAAE,EAAE;QACF,WAAW,EAAE,yCAAyC;QACtD,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,aAAa;QACzB,gBAAgB,EAAE,mCAAmC;QACrD,YAAY,EAAE;YACZ,wDAAwD;YACxD,yEAAyE;YACzE,kFAAkF;YAClF,2BAA2B;YAC3B,+EAA+E;SAChF;QACD,aAAa,EAAE,OAAO;KACvB;IAED,UAAU,CAAC,GAAG,EAAE,KAAK;QACnB,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;QAEnC,OAAO;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI,QAAQ,IAAI,MAAM,EAAE;gBACjC,GAAG;aACJ;YACD,KAAK,EAAE;gBACL,OAAO,EAAE;oBACP,GAAG;oBACH,OAAO,EAAE,aAAa;oBACtB,MAAM,EAAE,MAAM;iBACf;gBACD,MAAM,EAAE;oBACN,2CAA2C;oBAC3C,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;oBAC3D,4DAA4D;oBAC5D,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;iBAC7E;aACF;SACF,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,IAAI;QACd,MAAM,KAAK,GAAG,IAA8E,CAAC;QAC7F,OAAO;YACL,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,iBAAiB;YAClD,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG;SAC3B,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,GAAG;QACd,OAAO,GAAG,CAAC;IACb,CAAC;IAED,gBAAgB;QACd,OAAO,qDAAqD,CAAC;IAC/D,CAAC;IAED,KAAK,EAAE;QACL,EAAE,GAAG,EAAE,gCAAgC,EAAE,WAAW,EAAE,IAAI,EAAE;QAC5D,EAAE,GAAG,EAAE,6BAA6B,EAAE,WAAW,EAAE,IAAI,EAAE;QACzD,EAAE,GAAG,EAAE,4BAA4B,EAAE,WAAW,EAAE,IAAI,EAAE;QACxD,EAAE,GAAG,EAAE,2BAA2B,EAAE,WAAW,EAAE,KAAK,EAAE;QACxD,EAAE,GAAG,EAAE,+BAA+B,EAAE,WAAW,EAAE,KAAK,EAAE;KAC7D;CACF,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ServiceProvider } from "./types.js";
2
+ /**
3
+ * Bluesky service provider
4
+ *
5
+ * Users prove ownership of another Bluesky account by adding their DID to the profile bio.
6
+ * The claim URI is the bsky.app profile URL.
7
+ */
8
+ declare const bsky: ServiceProvider;
9
+ export default bsky;
10
+ //# sourceMappingURL=bsky.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bsky.d.ts","sourceRoot":"","sources":["../../src/serviceProviders/bsky.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,QAAA,MAAM,IAAI,EAAE,eA8DX,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bluesky service provider
3
+ *
4
+ * Users prove ownership of another Bluesky account by adding their DID to the profile bio.
5
+ * The claim URI is the bsky.app profile URL.
6
+ */
7
+ const bsky = {
8
+ id: "bsky",
9
+ name: "Bluesky",
10
+ homepage: "https://bsky.app",
11
+ // Match Bluesky profile URLs: https://bsky.app/profile/handle or did
12
+ reUri: /^https:\/\/bsky\.app\/profile\/([^/]+)\/?$/,
13
+ isAmbiguous: false,
14
+ ui: {
15
+ description: "Link another Bluesky account",
16
+ icon: "cloud",
17
+ inputLabel: "Profile URL",
18
+ inputPlaceholder: "https://bsky.app/profile/username.bsky.social",
19
+ instructions: [
20
+ "Log into the Bluesky account you want to link",
21
+ "Go to **Settings** → **Edit Profile**",
22
+ "Add your DID to your **bio** (the verification DID, not this account's DID)",
23
+ "Save your profile changes",
24
+ "Paste the profile URL below",
25
+ ],
26
+ proofTemplate: "{did}",
27
+ },
28
+ processURI(uri, match) {
29
+ const [, handle] = match;
30
+ return {
31
+ profile: {
32
+ display: handle.startsWith("did:") ? handle : `@${handle}`,
33
+ uri,
34
+ },
35
+ proof: {
36
+ request: {
37
+ uri: `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(handle)}`,
38
+ fetcher: "http",
39
+ format: "json",
40
+ },
41
+ target: [
42
+ // Check profile description/bio
43
+ { path: ["description"], relation: "contains", format: "text" },
44
+ ],
45
+ },
46
+ };
47
+ },
48
+ getProofText(did) {
49
+ return did;
50
+ },
51
+ getProofLocation() {
52
+ return `Add to your profile bio`;
53
+ },
54
+ tests: [
55
+ { uri: "https://bsky.app/profile/alice.bsky.social", shouldMatch: true },
56
+ { uri: "https://bsky.app/profile/did:plc:abc123", shouldMatch: true },
57
+ { uri: "https://bsky.app/profile/alice.bsky.social/", shouldMatch: true },
58
+ { uri: "https://bsky.app/profile/alice/post/123", shouldMatch: false },
59
+ { uri: "https://bsky.social/profile/alice", shouldMatch: false },
60
+ ],
61
+ };
62
+ export default bsky;
63
+ //# sourceMappingURL=bsky.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bsky.js","sourceRoot":"","sources":["../../src/serviceProviders/bsky.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,IAAI,GAAoB;IAC5B,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,kBAAkB;IAE5B,qEAAqE;IACrE,KAAK,EAAE,4CAA4C;IAEnD,WAAW,EAAE,KAAK;IAElB,EAAE,EAAE;QACF,WAAW,EAAE,8BAA8B;QAC3C,IAAI,EAAE,OAAO;QACb,UAAU,EAAE,aAAa;QACzB,gBAAgB,EAAE,+CAA+C;QACjE,YAAY,EAAE;YACZ,+CAA+C;YAC/C,uCAAuC;YACvC,6EAA6E;YAC7E,2BAA2B;YAC3B,6BAA6B;SAC9B;QACD,aAAa,EAAE,OAAO;KACvB;IAED,UAAU,CAAC,GAAG,EAAE,KAAK;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;QAEzB,OAAO;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE;gBAC1D,GAAG;aACJ;YACD,KAAK,EAAE;gBACL,OAAO,EAAE;oBACP,GAAG,EAAE,oEAAoE,kBAAkB,CAAC,MAAM,CAAC,EAAE;oBACrG,OAAO,EAAE,MAAM;oBACf,MAAM,EAAE,MAAM;iBACf;gBACD,MAAM,EAAE;oBACN,gCAAgC;oBAChC,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;iBAChE;aACF;SACF,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,GAAG;QACd,OAAO,GAAG,CAAC;IACb,CAAC;IAED,gBAAgB;QACd,OAAO,yBAAyB,CAAC;IACnC,CAAC;IAED,KAAK,EAAE;QACL,EAAE,GAAG,EAAE,4CAA4C,EAAE,WAAW,EAAE,IAAI,EAAE;QACxE,EAAE,GAAG,EAAE,yCAAyC,EAAE,WAAW,EAAE,IAAI,EAAE;QACrE,EAAE,GAAG,EAAE,6CAA6C,EAAE,WAAW,EAAE,IAAI,EAAE;QACzE,EAAE,GAAG,EAAE,yCAAyC,EAAE,WAAW,EAAE,KAAK,EAAE;QACtE,EAAE,GAAG,EAAE,mCAAmC,EAAE,WAAW,EAAE,KAAK,EAAE;KACjE;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}