@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.
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/profile.d.ts.map +1 -1
- package/dist/profile.js +29 -4
- package/dist/profile.js.map +1 -1
- package/dist/recipes/index.d.ts +1 -0
- package/dist/recipes/index.d.ts.map +1 -1
- package/dist/recipes/index.js +1 -0
- package/dist/recipes/index.js.map +1 -1
- package/dist/recipes/tangled.d.ts +10 -0
- package/dist/recipes/tangled.d.ts.map +1 -0
- package/dist/recipes/tangled.js +47 -0
- package/dist/recipes/tangled.js.map +1 -0
- package/dist/serviceProviders/index.d.ts +4 -2
- package/dist/serviceProviders/index.d.ts.map +1 -1
- package/dist/serviceProviders/index.js +5 -1
- package/dist/serviceProviders/index.js.map +1 -1
- package/dist/serviceProviders/npm.d.ts.map +1 -1
- package/dist/serviceProviders/npm.js +1 -0
- package/dist/serviceProviders/npm.js.map +1 -1
- package/dist/serviceProviders/pgp.d.ts +16 -0
- package/dist/serviceProviders/pgp.d.ts.map +1 -0
- package/dist/serviceProviders/pgp.js +157 -0
- package/dist/serviceProviders/pgp.js.map +1 -0
- package/dist/serviceProviders/tangled.d.ts +12 -0
- package/dist/serviceProviders/tangled.d.ts.map +1 -0
- package/dist/serviceProviders/tangled.js +75 -0
- package/dist/serviceProviders/tangled.js.map +1 -0
- package/dist/serviceProviders/types.d.ts +19 -0
- package/dist/serviceProviders/types.d.ts.map +1 -1
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -1
- package/src/profile.ts +35 -5
- package/src/recipes/index.ts +1 -0
- package/src/recipes/tangled.ts +48 -0
- package/src/serviceProviders/index.ts +6 -2
- package/src/serviceProviders/npm.ts +1 -0
- package/src/serviceProviders/pgp.ts +181 -0
- package/src/serviceProviders/tangled.ts +87 -0
- package/src/serviceProviders/types.ts +20 -0
- 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;
|
|
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,UAAU;IACzB,sFAAsF;IACtF,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;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;IACtB,wDAAwD;IACxD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,WAAW,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CAC/B;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"}
|
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
|
-
/**
|
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,gEAAgE;IAChE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,
|
|
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":"
|
|
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
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
|
|
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
|
|
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);
|
package/src/recipes/index.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
/**
|