@keytrace/runner 0.0.10 → 0.0.12

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 (46) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/profile.d.ts.map +1 -1
  6. package/dist/profile.js +29 -4
  7. package/dist/profile.js.map +1 -1
  8. package/dist/recipes/index.d.ts +1 -0
  9. package/dist/recipes/index.d.ts.map +1 -1
  10. package/dist/recipes/index.js +1 -0
  11. package/dist/recipes/index.js.map +1 -1
  12. package/dist/recipes/tangled.d.ts +10 -0
  13. package/dist/recipes/tangled.d.ts.map +1 -0
  14. package/dist/recipes/tangled.js +47 -0
  15. package/dist/recipes/tangled.js.map +1 -0
  16. package/dist/serviceProviders/index.d.ts +4 -2
  17. package/dist/serviceProviders/index.d.ts.map +1 -1
  18. package/dist/serviceProviders/index.js +5 -1
  19. package/dist/serviceProviders/index.js.map +1 -1
  20. package/dist/serviceProviders/npm.d.ts.map +1 -1
  21. package/dist/serviceProviders/npm.js +1 -0
  22. package/dist/serviceProviders/npm.js.map +1 -1
  23. package/dist/serviceProviders/pgp.d.ts +16 -0
  24. package/dist/serviceProviders/pgp.d.ts.map +1 -0
  25. package/dist/serviceProviders/pgp.js +157 -0
  26. package/dist/serviceProviders/pgp.js.map +1 -0
  27. package/dist/serviceProviders/tangled.d.ts +12 -0
  28. package/dist/serviceProviders/tangled.d.ts.map +1 -0
  29. package/dist/serviceProviders/tangled.js +75 -0
  30. package/dist/serviceProviders/tangled.js.map +1 -0
  31. package/dist/serviceProviders/types.d.ts +19 -0
  32. package/dist/serviceProviders/types.d.ts.map +1 -1
  33. package/dist/types.d.ts +13 -1
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/index.ts +2 -1
  38. package/src/profile.ts +35 -5
  39. package/src/recipes/index.ts +1 -0
  40. package/src/recipes/tangled.ts +48 -0
  41. package/src/serviceProviders/index.ts +6 -2
  42. package/src/serviceProviders/npm.ts +1 -0
  43. package/src/serviceProviders/pgp.ts +181 -0
  44. package/src/serviceProviders/tangled.ts +87 -0
  45. package/src/serviceProviders/types.ts +20 -0
  46. package/src/types.ts +10 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tangled.js","sourceRoot":"","sources":["../../src/serviceProviders/tangled.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,OAAO,GAAoB;IAC/B,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,qBAAqB;IAE/B,+EAA+E;IAC/E,KAAK,EAAE,4DAA4D;IAEnE,WAAW,EAAE,KAAK;IAElB,EAAE,EAAE;QACF,WAAW,EAAE,2BAA2B;QACxC,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,YAAY;QACxB,gBAAgB,EAAE,gDAAgD;QAClE,YAAY,EAAE;YACZ,kFAAkF;YAClF,+BAA+B;YAC/B,oDAAoD;YACpD,yCAAyC;SAC1C;QACD,aAAa,EAAE,wBAAwB;KACxC;IAED,UAAU,CAAC,GAAG,EAAE,KAAK;QACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;QAErC,OAAO;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI,QAAQ,EAAE;gBACvB,GAAG,EAAE,uBAAuB,QAAQ,EAAE;aACvC;YACD,KAAK,EAAE;gBACL,OAAO,EAAE;oBACP,GAAG,EAAE,+BAA+B,QAAQ,IAAI,QAAQ,MAAM;oBAC9D,OAAO,EAAE,MAAM;oBACf,MAAM,EAAE,MAAM;iBACf;gBACD,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,CAAC,KAAK,CAAC;wBACb,QAAQ,EAAE,UAAU;wBACpB,MAAM,EAAE,MAAM;qBACf;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,IAAI,EAAE,KAAK;QACrB,MAAM,CAAC,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;QAE3B,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,UAAU,EAAE,uBAAuB,QAAQ,EAAE;SAC9C,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,GAAG;QACd,OAAO,sCAAsC,GAAG,EAAE,CAAC;IACrD,CAAC;IAED,gBAAgB;QACd,OAAO,oFAAoF,CAAC;IAC9F,CAAC;IAED,KAAK,EAAE;QACL,EAAE,GAAG,EAAE,6CAA6C,EAAE,WAAW,EAAE,IAAI,EAAE;QACzE,EAAE,GAAG,EAAE,mDAAmD,EAAE,WAAW,EAAE,IAAI,EAAE;QAC/E,EAAE,GAAG,EAAE,8CAA8C,EAAE,WAAW,EAAE,IAAI,EAAE;QAC1E,EAAE,GAAG,EAAE,2BAA2B,EAAE,WAAW,EAAE,KAAK,EAAE;QACxD,EAAE,GAAG,EAAE,0BAA0B,EAAE,WAAW,EAAE,KAAK,EAAE;KACxD;CACF,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -49,6 +49,21 @@ export interface ProcessedURI {
49
49
  target: ProofTarget[];
50
50
  };
51
51
  }
52
+ /**
53
+ * An additional input field for the add claim wizard (beyond the main claim URI)
54
+ */
55
+ export interface ExtraInput {
56
+ /** Unique key used as placeholder in templates, e.g. "fingerprint" → {fingerprint} */
57
+ key: string;
58
+ /** Label displayed above the input */
59
+ label: string;
60
+ /** Placeholder text */
61
+ placeholder: string;
62
+ /** Optional regex pattern for validation */
63
+ pattern?: string;
64
+ /** Validation error message shown when pattern doesn't match */
65
+ patternError?: string;
66
+ }
52
67
  /**
53
68
  * UI configuration for the add claim wizard
54
69
  */
@@ -67,6 +82,10 @@ export interface ServiceProviderUI {
67
82
  instructions: string[];
68
83
  /** Template for proof content. Supports {did} and {handle} placeholders */
69
84
  proofTemplate: string;
85
+ /** Additional input fields beyond the main claim URI */
86
+ extraInputs?: ExtraInput[];
87
+ /** How to render the icon: "badge" (in circle bg, default) or "raw" (standalone SVG) */
88
+ iconDisplay?: "badge" | "raw";
70
89
  }
71
90
  /**
72
91
  * A service provider that can verify identity claims
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/serviceProviders/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC/C,6BAA6B;IAC7B,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,+BAA+B;IAC/B,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,OAAO,EAAE;QACP,6DAA6D;QAC7D,OAAO,EAAE,MAAM,CAAC;QAChB,yCAAyC;QACzC,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,wCAAwC;IACxC,KAAK,EAAE;QACL,OAAO,EAAE,YAAY,CAAC;QACtB,MAAM,EAAE,WAAW,EAAE,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qDAAqD;IACrD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,QAAQ,EAAE,MAAM,CAAC;IAEjB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IAEd,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,gDAAgD;IAChD,EAAE,EAAE,iBAAiB,CAAC;IAEtB,mDAAmD;IACnD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,YAAY,CAAC;IAE/D,wEAAwE;IACxE,WAAW,CAAC,CACV,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,gBAAgB,GACtB;QACD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF,2DAA2D;IAC3D,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEnD,+DAA+D;IAC/D,gBAAgB,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC;IAEnD,gCAAgC;IAChC,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,OAAO,CAAC;KACtB,EAAE,CAAC;CACL"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/serviceProviders/types.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -54,6 +54,7 @@ export interface ProfileData {
54
54
  did: string;
55
55
  handle: string;
56
56
  displayName?: string;
57
+ description?: string;
57
58
  avatar?: string;
58
59
  claims: ClaimData[];
59
60
  }
@@ -68,10 +69,21 @@ export interface ClaimData {
68
69
  createdAt: string;
69
70
  rkey: string;
70
71
  identity?: IdentityMetadata;
71
- /** Signing key reference from the claim record */
72
+ /** @deprecated Use sigs instead. Retained for backwards compat with old records. */
72
73
  sig?: {
73
74
  src?: string;
74
75
  };
76
+ /** Signing key references from the claim record (new format) */
77
+ sigs?: Array<{
78
+ kid?: string;
79
+ src?: string;
80
+ }>;
81
+ /** Current verification status. Absent on legacy records, treated as "verified". */
82
+ status?: "verified" | "failed" | "retracted";
83
+ /** Timestamp of the most recent successful re-verification */
84
+ lastVerifiedAt?: string;
85
+ /** Timestamp when the claim last failed re-verification or was retracted */
86
+ failedAt?: string;
75
87
  }
76
88
  /**
77
89
  * Options for verification operations
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAuGA;;GAEG;AACH,MAAM,CAAN,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,oCAAqB,CAAA;IACrB,gCAAiB,CAAA;IACjB,8BAAe,CAAA;AACjB,CAAC,EANW,WAAW,KAAX,WAAW,QAMtB"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgHA;;GAEG;AACH,MAAM,CAAN,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,oCAAqB,CAAA;IACrB,gCAAiB,CAAA;IACjB,8BAAe,CAAA;AACjB,CAAC,EANW,WAAW,KAAX,WAAW,QAMtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keytrace/runner",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "files": [
5
5
  "src",
6
6
  "dist"
package/src/index.ts CHANGED
@@ -40,6 +40,7 @@ export { dnsTxt } from "./actions/dns-txt.js";
40
40
  // Built-in recipes
41
41
  export { githubGistRecipe } from "./recipes/github-gist.js";
42
42
  export { dnsTxtRecipe } from "./recipes/dns-txt.js";
43
+ export { tangledRecipe } from "./recipes/tangled.js";
43
44
 
44
45
  // Claim & Profile (from runner)
45
46
  export { createClaim, matchClaim, verifyClaim, isClaimAmbiguous, getMatchedProvider, isValidDid } from "./claim.js";
@@ -52,7 +53,7 @@ export { COLLECTION_NSID, DEFAULT_TIMEOUT, PUBLIC_API_URL, PLC_DIRECTORY_URL } f
52
53
 
53
54
  // Service providers
54
55
  export * as serviceProviders from "./serviceProviders/index.js";
55
- export type { ServiceProvider, ServiceProviderMatch, ServiceProviderUI, ProofTarget, ProofRequest, ProcessedURI } from "./serviceProviders/types.js";
56
+ export type { ServiceProvider, ServiceProviderMatch, ServiceProviderUI, ExtraInput, ProofTarget, ProofRequest, ProcessedURI } from "./serviceProviders/types.js";
56
57
 
57
58
  // Fetchers
58
59
  export * as fetchers from "./fetchers/index.js";
package/src/profile.ts CHANGED
@@ -7,6 +7,12 @@ import type { ProfileData, ClaimData, VerifyOptions, IdentityMetadata, ProfileOp
7
7
  /** Default trusted signer handles */
8
8
  const DEFAULT_TRUSTED_SIGNERS = ["keytrace.dev"];
9
9
 
10
+ /** Find the attestation sig (kid starting with "attest:"), falling back to first sig or legacy sig */
11
+ function getAttestSrc(sigs?: Array<{ kid?: string; src?: string }>, sig?: { src?: string }): string | undefined {
12
+ const attest = sigs?.find((s) => s.kid?.startsWith("attest:"));
13
+ return attest?.src ?? sigs?.[0]?.src ?? sig?.src;
14
+ }
15
+
10
16
  /**
11
17
  * Extract the DID from an AT URI (at://did/collection/rkey)
12
18
  */
@@ -130,13 +136,14 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
130
136
 
131
137
  // Fetch Bluesky profile for display info via public API (not PDS)
132
138
  // The PDS doesn't serve app.bsky.actor.getProfile - only the AppView does
133
- let bskyProfile: { handle: string; displayName?: string; avatar?: string } | null = null;
139
+ let bskyProfile: { handle: string; displayName?: string; description?: string; avatar?: string } | null = null;
134
140
  try {
135
141
  const publicAgent = new AtpAgent({ service: PUBLIC_API_URL });
136
142
  const profileRes = await publicAgent.getProfile({ actor: did });
137
143
  bskyProfile = {
138
144
  handle: profileRes.data.handle,
139
145
  displayName: profileRes.data.displayName,
146
+ description: profileRes.data.description,
140
147
  avatar: profileRes.data.avatar,
141
148
  };
142
149
  } catch (err: unknown) {
@@ -147,6 +154,20 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
147
154
  }
148
155
  }
149
156
 
157
+ // Fetch keytrace profile record (dev.keytrace.profile/self) for bio/displayName overrides
158
+ let ktProfile: { displayName?: string; bio?: string } | null = null;
159
+ try {
160
+ const record = await agent.com.atproto.repo.getRecord({
161
+ repo: did,
162
+ collection: "dev.keytrace.profile",
163
+ rkey: "self",
164
+ });
165
+ const value = record.data.value as { displayName?: string; bio?: string };
166
+ ktProfile = value;
167
+ } catch {
168
+ // No keytrace profile record — that's fine
169
+ }
170
+
150
171
  // List all claim records with cursor-based pagination
151
172
  const claims: ClaimData[] = [];
152
173
  try {
@@ -167,6 +188,10 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
167
188
  createdAt?: string;
168
189
  identity?: IdentityMetadata;
169
190
  sig?: { src?: string };
191
+ sigs?: Array<{ kid?: string; src?: string }>;
192
+ status?: "verified" | "failed" | "retracted";
193
+ lastVerifiedAt?: string;
194
+ failedAt?: string;
170
195
  };
171
196
  if (value.claimUri) {
172
197
  claims.push({
@@ -177,7 +202,11 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
177
202
  createdAt: value.createdAt ?? new Date().toISOString(),
178
203
  rkey: parseAtUriRkey(record.uri),
179
204
  identity: value.identity,
180
- sig: value.sig,
205
+ sig: value.sigs?.find((s) => s.kid?.startsWith("attest:")) ?? value.sigs?.[0] ?? value.sig,
206
+ sigs: value.sigs,
207
+ status: value.status,
208
+ lastVerifiedAt: value.lastVerifiedAt,
209
+ failedAt: value.failedAt,
181
210
  });
182
211
  }
183
212
  }
@@ -194,7 +223,7 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
194
223
  // Build claim instances, marking untrusted signers as FAILED
195
224
  const claimInstances = claims.map((c) => {
196
225
  const state = createClaim(c.uri, did);
197
- const trustError = checkSignerTrust(c.sig?.src, trustedDids);
226
+ const trustError = checkSignerTrust(getAttestSrc(c.sigs, c.sig), trustedDids);
198
227
  if (trustError) {
199
228
  state.status = ClaimStatus.FAILED;
200
229
  state.errors.push(trustError);
@@ -205,7 +234,8 @@ async function fetchWithAgent(agent: AtpAgent, did: string, opts?: ProfileOption
205
234
  return {
206
235
  did,
207
236
  handle: bskyProfile?.handle ?? did,
208
- displayName: bskyProfile?.displayName,
237
+ displayName: ktProfile?.displayName || bskyProfile?.displayName,
238
+ description: ktProfile?.bio || bskyProfile?.description,
209
239
  avatar: bskyProfile?.avatar,
210
240
  claims,
211
241
  claimInstances,
@@ -269,7 +299,7 @@ export async function verifyAllClaims(profile: FetchedProfile, opts?: VerifyOpti
269
299
 
270
300
  // Check signing key provenance
271
301
  const claimData = profile.claims[i];
272
- const trustError = checkSignerTrust(claimData?.sig?.src, trustedDids);
302
+ const trustError = checkSignerTrust(claimData ? getAttestSrc(claimData.sigs, claimData.sig) : undefined, trustedDids);
273
303
  if (trustError) {
274
304
  claim.status = ClaimStatus.FAILED;
275
305
  claim.errors.push(trustError);
@@ -1,2 +1,3 @@
1
1
  export { githubGistRecipe } from "./github-gist.js";
2
2
  export { dnsTxtRecipe } from "./dns-txt.js";
3
+ export { tangledRecipe } from "./tangled.js";
@@ -0,0 +1,48 @@
1
+ import type { Recipe } from "../types.js";
2
+
3
+ /**
4
+ * Built-in recipe: Tangled Account verification via string.
5
+ *
6
+ * The user creates a string (Tangled's gist equivalent) containing
7
+ * their DID, then provides the string URL for verification.
8
+ * Raw content is fetched from {stringUrl}/raw.
9
+ */
10
+ export const tangledRecipe: Recipe = {
11
+ $type: "dev.keytrace.recipe",
12
+ type: "tangled",
13
+ version: 1,
14
+ displayName: "Tangled Account (via String)",
15
+ params: [
16
+ {
17
+ key: "stringUrl",
18
+ label: "String URL",
19
+ type: "url",
20
+ placeholder: "https://tangled.org/strings/username/abc123...",
21
+ pattern: "^https://tangled\\.org/strings/([^/]+)/([a-z0-9]+)/?$",
22
+ extractFrom: "^https://tangled\\.org/strings/([^/]+)/",
23
+ },
24
+ ],
25
+ instructions: {
26
+ steps: [
27
+ "Go to [tangled.org/strings](https://tangled.org/strings) and create a new string",
28
+ "Name the file `keytrace.json`",
29
+ "Paste the verification content below into the file",
30
+ "Save the string and paste the URL below",
31
+ ],
32
+ proofTemplate: '{\n "did": "{did}"\n}',
33
+ proofLocation: "Public string on tangled.org",
34
+ },
35
+ verification: {
36
+ steps: [
37
+ {
38
+ action: "http-get",
39
+ url: "{stringUrl}/raw",
40
+ },
41
+ {
42
+ action: "json-path",
43
+ selector: "$.did",
44
+ expect: "equals:{did}",
45
+ },
46
+ ],
47
+ },
48
+ };
@@ -3,9 +3,11 @@ import dns from "./dns.js";
3
3
  import activitypub from "./activitypub.js";
4
4
  import bsky from "./bsky.js";
5
5
  import npm from "./npm.js";
6
+ import tangled from "./tangled.js";
7
+ import pgp from "./pgp.js";
6
8
  import type { ServiceProvider, ServiceProviderMatch } from "./types.js";
7
9
 
8
- export type { ServiceProvider, ServiceProviderMatch, ServiceProviderUI, ProofTarget, ProofRequest, ProcessedURI } from "./types.js";
10
+ export type { ServiceProvider, ServiceProviderMatch, ServiceProviderUI, ExtraInput, ProofTarget, ProofRequest, ProcessedURI } from "./types.js";
9
11
 
10
12
  const providers: Record<string, ServiceProvider> = {
11
13
  github,
@@ -13,6 +15,8 @@ const providers: Record<string, ServiceProvider> = {
13
15
  activitypub,
14
16
  bsky,
15
17
  npm,
18
+ tangled,
19
+ pgp,
16
20
  };
17
21
 
18
22
  /**
@@ -62,4 +66,4 @@ export function getProofTextForProvider(providerId: string, did: string, handle?
62
66
  return provider?.getProofText(did, handle);
63
67
  }
64
68
 
65
- export { github, dns, activitypub, bsky, npm };
69
+ export { github, dns, activitypub, bsky, npm, tangled, pgp };
@@ -28,6 +28,7 @@ const npm: ServiceProvider = {
28
28
  ui: {
29
29
  description: "Link via an npm package",
30
30
  icon: "npm",
31
+ iconDisplay: "raw",
31
32
  inputLabel: "npm Package URL",
32
33
  inputPlaceholder: "https://www.npmjs.com/package/keytrace-yourhandle",
33
34
  inputDefaultTemplate: "https://www.npmjs.com/package/keytrace-{slugHandle}",
@@ -0,0 +1,181 @@
1
+ import type { ServiceProvider } from "./types.js";
2
+
3
+ /**
4
+ * PGP Key service provider
5
+ *
6
+ * Users prove ownership of a PGP key by clearsigning a proof message
7
+ * containing their DID and fingerprint, then hosting it at a URL.
8
+ * The claim URI format is: pgp:https://...
9
+ *
10
+ * Supported proof hosting:
11
+ * - GitHub Gists (fetches via API)
12
+ * - Tangled Strings (fetches raw content)
13
+ * - Any other HTTPS URL (fetches as plain text)
14
+ */
15
+ const pgp: ServiceProvider = {
16
+ id: "pgp",
17
+ name: "PGP Key",
18
+ homepage: "https://www.openpgp.org",
19
+
20
+ // Match pgp: prefixed URLs
21
+ reUri: /^pgp:(https:\/\/.+)$/,
22
+
23
+ isAmbiguous: false,
24
+
25
+ ui: {
26
+ description: "Prove ownership of a PGP key",
27
+ icon: "shield",
28
+ inputLabel: "Proof URL",
29
+ inputPlaceholder: "https://gist.github.com/username/abc123...",
30
+ instructions: [
31
+ "Install [GPG tools](https://gist.github.com/jfrobbins/5c2dbceb81c33afc5b0bcbe0d3343692#1-installing-gpg)",
32
+ "Enter your PGP fingerprint to set up the proof (run `gpg --fingerprint` to find it)",
33
+ "Copy the proof text and save it to a file (e.g. `proof.txt`)",
34
+ "Clearsign the file with your PGP key: `gpg --clearsign proof.txt`",
35
+ "Host the signed output (`proof.txt.asc`) at a public URL — a [GitHub Gist](https://gist.github.com) or [Tangled String](https://tangled.org/strings/new) works well",
36
+ "Paste the URL below",
37
+ ],
38
+ proofTemplate: "Verifying my PGP key on keytrace\n\ndid: {did}\nfingerprint: {fingerprint}",
39
+ extraInputs: [
40
+ {
41
+ key: "fingerprint",
42
+ label: "PGP Fingerprint",
43
+ placeholder: "AB12 CD34 EF56 7890 1234 5678 9ABC DEF0 1234 5678",
44
+ pattern: "^[A-Fa-f0-9 ]{16,}$",
45
+ patternError: "Enter a valid PGP fingerprint (hex characters)",
46
+ },
47
+ ],
48
+ },
49
+
50
+ processURI(uri, match) {
51
+ const [, proofUrl] = match;
52
+
53
+ // Detect hosting platform to choose fetch strategy
54
+ const gistMatch = proofUrl.match(/^https:\/\/gist\.github\.com\/([^/]+)\/([a-f0-9]+)\/?$/);
55
+ const tangledMatch = proofUrl.match(/^https:\/\/tangled\.org\/strings\/([^/]+)\/([a-z0-9]+)\/?$/);
56
+
57
+ if (gistMatch) {
58
+ const [, , gistId] = gistMatch;
59
+ return {
60
+ profile: {
61
+ display: "PGP Key",
62
+ uri: proofUrl,
63
+ },
64
+ proof: {
65
+ request: {
66
+ uri: `https://api.github.com/gists/${gistId}`,
67
+ fetcher: "http",
68
+ format: "json",
69
+ options: {
70
+ headers: { Accept: "application/vnd.github.v3+json" },
71
+ },
72
+ },
73
+ target: [{ path: ["files", "*", "content"], relation: "contains", format: "text" }],
74
+ },
75
+ };
76
+ }
77
+
78
+ if (tangledMatch) {
79
+ const [, username, stringId] = tangledMatch;
80
+ return {
81
+ profile: {
82
+ display: "PGP Key",
83
+ uri: proofUrl,
84
+ },
85
+ proof: {
86
+ request: {
87
+ uri: `https://tangled.org/strings/${username}/${stringId}/raw`,
88
+ fetcher: "http",
89
+ format: "text",
90
+ },
91
+ target: [{ path: [], relation: "contains", format: "text" }],
92
+ },
93
+ };
94
+ }
95
+
96
+ // Generic URL: fetch as plain text
97
+ return {
98
+ profile: {
99
+ display: "PGP Key",
100
+ uri: proofUrl,
101
+ },
102
+ proof: {
103
+ request: {
104
+ uri: proofUrl,
105
+ fetcher: "http",
106
+ format: "text",
107
+ },
108
+ target: [{ path: [], relation: "contains", format: "text" }],
109
+ },
110
+ };
111
+ },
112
+
113
+ postprocess(data, match) {
114
+ const [, proofUrl] = match;
115
+
116
+ // Try to extract fingerprint from the proof text
117
+ let text = "";
118
+ if (typeof data === "string") {
119
+ text = data;
120
+ } else if (data && typeof data === "object") {
121
+ // For gist JSON, search through file contents
122
+ const gist = data as { files?: Record<string, { content?: string }>; owner?: { avatar_url?: string; login?: string } };
123
+ if (gist.files) {
124
+ for (const file of Object.values(gist.files)) {
125
+ if (file.content) {
126
+ text += file.content + "\n";
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ const fpMatch = text.match(/fingerprint:\s*([A-Fa-f0-9 ]{16,})/);
133
+ const fingerprint = fpMatch ? fpMatch[1].replace(/\s+/g, "").toLowerCase() : undefined;
134
+
135
+ const result: {
136
+ subject?: string;
137
+ avatarUrl?: string;
138
+ profileUrl?: string;
139
+ displayName?: string;
140
+ } = {
141
+ subject: fingerprint,
142
+ profileUrl: proofUrl,
143
+ displayName: "PGP Key",
144
+ };
145
+
146
+ // Extract avatar from GitHub gists
147
+ const gistMatch = proofUrl.match(/^https:\/\/gist\.github\.com\/([^/]+)\//);
148
+ if (gistMatch && data && typeof data === "object") {
149
+ const gist = data as { owner?: { avatar_url?: string; login?: string } };
150
+ if (gist.owner?.avatar_url) {
151
+ result.avatarUrl = gist.owner.avatar_url;
152
+ }
153
+ }
154
+
155
+ // Extract username from Tangled strings
156
+ const tangledMatch = proofUrl.match(/^https:\/\/tangled\.org\/strings\/([^/]+)\//);
157
+ if (tangledMatch) {
158
+ result.displayName = `PGP Key (${tangledMatch[1]})`;
159
+ }
160
+
161
+ return result;
162
+ },
163
+
164
+ getProofText(did) {
165
+ return `Verifying my PGP key on keytrace\n\ndid: ${did}\nfingerprint: {fingerprint}`;
166
+ },
167
+
168
+ getProofLocation() {
169
+ return "Host the clearsigned proof at a public URL (e.g. a GitHub Gist)";
170
+ },
171
+
172
+ tests: [
173
+ { uri: "pgp:https://gist.github.com/alice/abc123def456", shouldMatch: true },
174
+ { uri: "pgp:https://example.com/proof.txt", shouldMatch: true },
175
+ { uri: "pgp:https://tangled.org/strings/alice/abc123", shouldMatch: true },
176
+ { uri: "https://gist.github.com/alice/abc123", shouldMatch: false },
177
+ { uri: "pgp:http://example.com/proof.txt", shouldMatch: false },
178
+ ],
179
+ };
180
+
181
+ export default pgp;
@@ -0,0 +1,87 @@
1
+ import type { ServiceProvider } from "./types.js";
2
+
3
+ /**
4
+ * Tangled service provider
5
+ *
6
+ * Users prove ownership of their tangled.org account by creating a
7
+ * "string" (Tangled's equivalent of a gist) containing their DID.
8
+ * URL format: https://tangled.org/strings/<username>/<stringId>
9
+ * Raw content: https://tangled.org/strings/<username>/<stringId>/raw
10
+ */
11
+ const tangled: ServiceProvider = {
12
+ id: "tangled",
13
+ name: "Tangled",
14
+ homepage: "https://tangled.org",
15
+
16
+ // Match tangled.org string URLs: https://tangled.org/strings/username/stringId
17
+ reUri: /^https:\/\/tangled\.org\/strings\/([^/]+)\/([a-z0-9]+)\/?$/,
18
+
19
+ isAmbiguous: false,
20
+
21
+ ui: {
22
+ description: "Link via a Tangled string",
23
+ icon: "tangled",
24
+ iconDisplay: "raw",
25
+ inputLabel: "String URL",
26
+ inputPlaceholder: "https://tangled.org/strings/username/abc123...",
27
+ instructions: [
28
+ "Go to [tangled.org/strings](https://tangled.org/strings) and create a new string",
29
+ "Name the file `keytrace.json`",
30
+ "Paste the verification content below into the file",
31
+ "Save the string and paste the URL below",
32
+ ],
33
+ proofTemplate: '{\n "did": "{did}"\n}',
34
+ },
35
+
36
+ processURI(uri, match) {
37
+ const [, username, stringId] = match;
38
+
39
+ return {
40
+ profile: {
41
+ display: `@${username}`,
42
+ uri: `https://tangled.org/${username}`,
43
+ },
44
+ proof: {
45
+ request: {
46
+ uri: `https://tangled.org/strings/${username}/${stringId}/raw`,
47
+ fetcher: "http",
48
+ format: "json",
49
+ },
50
+ target: [
51
+ {
52
+ path: ["did"],
53
+ relation: "contains",
54
+ format: "text",
55
+ },
56
+ ],
57
+ },
58
+ };
59
+ },
60
+
61
+ postprocess(data, match) {
62
+ const [, username] = match;
63
+
64
+ return {
65
+ subject: username,
66
+ profileUrl: `https://tangled.org/${username}`,
67
+ };
68
+ },
69
+
70
+ getProofText(did) {
71
+ return `Verifying my identity on keytrace: ${did}`;
72
+ },
73
+
74
+ getProofLocation() {
75
+ return `Create a string on tangled.org with a keytrace.json file containing the proof text`;
76
+ },
77
+
78
+ tests: [
79
+ { uri: "https://tangled.org/strings/alice/abc123def", shouldMatch: true },
80
+ { uri: "https://tangled.org/strings/orta.io/3melbs7rkoz22", shouldMatch: true },
81
+ { uri: "https://tangled.org/strings/alice/abc123def/", shouldMatch: true },
82
+ { uri: "https://tangled.org/alice", shouldMatch: false },
83
+ { uri: "https://github.com/alice", shouldMatch: false },
84
+ ],
85
+ };
86
+
87
+ export default tangled;
@@ -53,6 +53,22 @@ export interface ProcessedURI {
53
53
  };
54
54
  }
55
55
 
56
+ /**
57
+ * An additional input field for the add claim wizard (beyond the main claim URI)
58
+ */
59
+ export interface ExtraInput {
60
+ /** Unique key used as placeholder in templates, e.g. "fingerprint" → {fingerprint} */
61
+ key: string;
62
+ /** Label displayed above the input */
63
+ label: string;
64
+ /** Placeholder text */
65
+ placeholder: string;
66
+ /** Optional regex pattern for validation */
67
+ pattern?: string;
68
+ /** Validation error message shown when pattern doesn't match */
69
+ patternError?: string;
70
+ }
71
+
56
72
  /**
57
73
  * UI configuration for the add claim wizard
58
74
  */
@@ -71,6 +87,10 @@ export interface ServiceProviderUI {
71
87
  instructions: string[];
72
88
  /** Template for proof content. Supports {did} and {handle} placeholders */
73
89
  proofTemplate: string;
90
+ /** Additional input fields beyond the main claim URI */
91
+ extraInputs?: ExtraInput[];
92
+ /** How to render the icon: "badge" (in circle bg, default) or "raw" (standalone SVG) */
93
+ iconDisplay?: "badge" | "raw";
74
94
  }
75
95
 
76
96
  /**
package/src/types.ts CHANGED
@@ -58,6 +58,7 @@ export interface ProfileData {
58
58
  did: string;
59
59
  handle: string;
60
60
  displayName?: string;
61
+ description?: string;
61
62
  avatar?: string;
62
63
  claims: ClaimData[];
63
64
  }
@@ -73,8 +74,16 @@ export interface ClaimData {
73
74
  createdAt: string;
74
75
  rkey: string;
75
76
  identity?: IdentityMetadata;
76
- /** Signing key reference from the claim record */
77
+ /** @deprecated Use sigs instead. Retained for backwards compat with old records. */
77
78
  sig?: { src?: string };
79
+ /** Signing key references from the claim record (new format) */
80
+ sigs?: Array<{ kid?: string; src?: string }>;
81
+ /** Current verification status. Absent on legacy records, treated as "verified". */
82
+ status?: "verified" | "failed" | "retracted";
83
+ /** Timestamp of the most recent successful re-verification */
84
+ lastVerifiedAt?: string;
85
+ /** Timestamp when the claim last failed re-verification or was retracted */
86
+ failedAt?: string;
78
87
  }
79
88
 
80
89
  /**