@thurinlabs/identity-kit 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -126,6 +126,14 @@ const { keyInfo, proofs, isLoading } = usePGPProofs(fingerprint, attestation.pgp
126
126
 
127
127
  `useThurinIdentity` wires this up for you from the current attestation.
128
128
 
129
+ ### useEnsHint
130
+
131
+ ```tsx
132
+ const { state, record, reason, isLoading } = useEnsHint('ben.thurinlabs.eth', identity.currentFingerprint)
133
+ ```
134
+
135
+ The name's `id.thurin` text record against the key the registry verifies for its address. `state` is `match`, `unset`, or `mismatch` (with a `reason`). The record is a discovery hint an ENS profile can show; the trust is in the claim. See [Point your ENS name at your claim](https://docs.thurin.id/#/guides/ens-record).
136
+
129
137
  ## `@thurinlabs/identity-kit/core` — no React
130
138
 
131
139
  Everything under "Core utilities" below is also published as its own entry point with no React, wagmi, or DOM dependency, for Node and worker consumers (the Thurin CLI and the share-card service use it):
@@ -174,7 +182,7 @@ await hasEmailUserID(armoredKey) // → true if any user ID contains an @
174
182
 
175
183
  **Published identity.** An attestation stores the armored key on-chain, permanently and publicly. Since 0.9.0 the intended shape is a key whose only user ID is a non-email one (any name — `thurin` is the suggestion), carrying the `proof@thurin.id` notations. `stripEmailUserIDs` produces that from a normal export; the stripped key still verifies (`verifyAttestation` needs at least one self-certified user ID, so a key with none is rejected) and keeps the notations on the user ID it retains. Proofs are then read from the on-chain key, never from a keyserver.
176
184
 
177
- `fetchKeyByFingerprint` / `fetchKeyByKeyId` (keys.openpgp.org) remain exported for key-ID → fingerprint resolution, but note that keyserver serves unverified-email keys as bare packets and drops non-email user IDs, so it cannot supply a published identity.
185
+ Nothing in the kit talks to a keyserver: keys come from the registry, and `thurin keyserver` / keys.thurin.id serve them over HKP for gpg.
178
186
 
179
187
  ### EFP & claims
180
188
 
@@ -195,6 +203,18 @@ getRegistry('sepolia') // → { chainId: 11155111, address, deployBlock, explore
195
203
 
196
204
  `REGISTRY_ABI` is the complete v2 ABI (reads and writes), so apps that publish claims use the same one. The v2 registry is deployed with CREATE2 and has the same address on every network.
197
205
 
206
+ ### ENS record (`id.thurin`)
207
+
208
+ ```ts
209
+ import { ensHintFor, ensHintValue, ensHintWrite, fetchEnsHint, ENS_HINT_KEY } from '@thurinlabs/identity-kit/core'
210
+
211
+ const hint = await fetchEnsHint(publicClient, 'ben.thurinlabs.eth', verifiedFingerprint) // { state: 'match' | 'unset' | 'mismatch', record, fingerprint, expected, reason? }
212
+ const call = ensHintWrite('ben.thurinlabs.eth', verifiedFingerprint) // { abi, functionName: 'setText', args: [namehash, 'id.thurin', 'FPR…'] }
213
+ const resolver = await publicClient.getEnsResolver({ name: call.name }) // look it up at write time; ENSv2 resolvers are per account
214
+ ```
215
+
216
+ `ensHintFor(record, fingerprint)` is the pure comparison; `ensHintValue` is the bare uppercase form Thurin writes.
217
+
198
218
  ### Fingerprints and key IDs
199
219
 
200
220
  The v2 registry takes raw fingerprint bytes and indexes by their hash and by long key ID:
@@ -1427,25 +1427,6 @@ async function hasEmailUserID(armoredKey) {
1427
1427
  return false;
1428
1428
  }
1429
1429
  }
1430
- var KEYSERVER_BASE = "https://keys.openpgp.org/vks/v1";
1431
- async function fetchKeyByFingerprint(fingerprint) {
1432
- try {
1433
- const resp = await fetch(`${KEYSERVER_BASE}/by-fingerprint/${fingerprint}`);
1434
- if (!resp.ok) return null;
1435
- return await resp.text();
1436
- } catch (e7) {
1437
- return null;
1438
- }
1439
- }
1440
- async function fetchKeyByKeyId(keyId) {
1441
- try {
1442
- const resp = await fetch(`${KEYSERVER_BASE}/by-keyid/${keyId}`);
1443
- if (!resp.ok) return null;
1444
- return await resp.text();
1445
- } catch (e8) {
1446
- return null;
1447
- }
1448
- }
1449
1430
 
1450
1431
  // src/core/efp.ts
1451
1432
  var EFP_API = "https://api.ethfollow.xyz/api/v1";
@@ -1473,7 +1454,7 @@ async function fetchEFPGraph(address) {
1473
1454
  top8 = (top8Data.following || []).map((f) => f.data);
1474
1455
  }
1475
1456
  return { followers, following, top8, hasEfp: !!listId || followers > 0 };
1476
- } catch (e9) {
1457
+ } catch (e7) {
1477
1458
  return null;
1478
1459
  }
1479
1460
  }
@@ -1716,6 +1697,42 @@ async function verifyProof(proof, fingerprint, neynarApiKey) {
1716
1697
  return fn(proof, fingerprint, neynarApiKey);
1717
1698
  }
1718
1699
 
1700
+ // src/core/ensHint.ts
1701
+ var _ens = require('viem/ens');
1702
+ var ENS_HINT_KEY = "id.thurin";
1703
+ function ensHintFor(record, verifiedFingerprint) {
1704
+ const raw = _nullishCoalesce(_optionalChain([record, 'optionalAccess', _28 => _28.trim, 'call', _29 => _29()]), () => ( ""));
1705
+ const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null;
1706
+ if (!raw) return { key: ENS_HINT_KEY, state: "unset", record: null, fingerprint: null, expected };
1707
+ const fingerprint = normalizeFingerprint(raw);
1708
+ if (!fingerprint) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint: null, expected, reason: "The record is not a PGP fingerprint" };
1709
+ if (!expected) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The address has no verified claim" };
1710
+ if (fingerprint === expected) return { key: ENS_HINT_KEY, state: "match", record: raw, fingerprint, expected };
1711
+ return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The record names a key this address has not claimed" };
1712
+ }
1713
+ function ensHintValue(fingerprint) {
1714
+ const fp = normalizeFingerprint(fingerprint);
1715
+ if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`);
1716
+ return fp.toUpperCase();
1717
+ }
1718
+ var ENS_TEXT_RESOLVER_ABI = [
1719
+ { type: "function", name: "text", stateMutability: "view", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }], outputs: [{ name: "", type: "string" }] },
1720
+ { type: "function", name: "setText", stateMutability: "nonpayable", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }, { name: "value", type: "string" }], outputs: [] }
1721
+ ];
1722
+ function ensHintWrite(name, fingerprint) {
1723
+ const normalized = _ens.normalize.call(void 0, name);
1724
+ return {
1725
+ abi: ENS_TEXT_RESOLVER_ABI,
1726
+ functionName: "setText",
1727
+ args: [_ens.namehash.call(void 0, normalized), ENS_HINT_KEY, ensHintValue(fingerprint)],
1728
+ name: normalized
1729
+ };
1730
+ }
1731
+ async function fetchEnsHint(client, name, verifiedFingerprint) {
1732
+ const record = await client.getEnsText({ name: _ens.normalize.call(void 0, name), key: ENS_HINT_KEY });
1733
+ return ensHintFor(record, verifiedFingerprint);
1734
+ }
1735
+
1719
1736
  // src/core/authorization.ts
1720
1737
 
1721
1738
  var EIP712_NAME = "Thurin PGPRegistry";
@@ -1854,5 +1871,9 @@ function recordKind(name) {
1854
1871
 
1855
1872
 
1856
1873
 
1857
- exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.NETWORKS = NETWORKS; exports.isNetworkName = isNetworkName; exports.getRegistry = getRegistry; exports.chainFor = chainFor; exports.normalizeFingerprint = normalizeFingerprint; exports.fingerprintToBytes = fingerprintToBytes; exports.bytesToFingerprint = bytesToFingerprint; exports.fingerprintHash = fingerprintHash; exports.keyIdOf = keyIdOf; exports.keyIdToBytes = keyIdToBytes; exports.parsePgpKey = parsePgpKey; exports.verifyAttestation = verifyAttestation; exports.stripEmailUserIDs = stripEmailUserIDs; exports.hasEmailUserID = hasEmailUserID; exports.fetchKeyByFingerprint = fetchKeyByFingerprint; exports.fetchKeyByKeyId = fetchKeyByKeyId; exports.fetchEFPGraph = fetchEFPGraph; exports.identifyProof = identifyProof; exports.displayUrl = displayUrl; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.verifyProof = verifyProof; exports.EIP712_NAME = EIP712_NAME; exports.EIP712_VERSION = EIP712_VERSION; exports.registryDomain = registryDomain; exports.AUTHORIZATION_TYPES = AUTHORIZATION_TYPES; exports.attestTypedData = attestTypedData; exports.reattestTypedData = reattestTypedData; exports.updateKeyTypedData = updateKeyTypedData; exports.revokeTypedData = revokeTypedData; exports.setRecordTypedData = setRecordTypedData; exports.authorizationDigest = authorizationDigest; exports.recordKind = recordKind;
1858
- //# sourceMappingURL=chunk-RC4PCCYV.cjs.map
1874
+
1875
+
1876
+
1877
+
1878
+ exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.NETWORKS = NETWORKS; exports.isNetworkName = isNetworkName; exports.getRegistry = getRegistry; exports.chainFor = chainFor; exports.normalizeFingerprint = normalizeFingerprint; exports.fingerprintToBytes = fingerprintToBytes; exports.bytesToFingerprint = bytesToFingerprint; exports.fingerprintHash = fingerprintHash; exports.keyIdOf = keyIdOf; exports.keyIdToBytes = keyIdToBytes; exports.parsePgpKey = parsePgpKey; exports.verifyAttestation = verifyAttestation; exports.stripEmailUserIDs = stripEmailUserIDs; exports.hasEmailUserID = hasEmailUserID; exports.fetchEFPGraph = fetchEFPGraph; exports.identifyProof = identifyProof; exports.displayUrl = displayUrl; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.verifyProof = verifyProof; exports.ENS_HINT_KEY = ENS_HINT_KEY; exports.ensHintFor = ensHintFor; exports.ensHintValue = ensHintValue; exports.ENS_TEXT_RESOLVER_ABI = ENS_TEXT_RESOLVER_ABI; exports.ensHintWrite = ensHintWrite; exports.fetchEnsHint = fetchEnsHint; exports.EIP712_NAME = EIP712_NAME; exports.EIP712_VERSION = EIP712_VERSION; exports.registryDomain = registryDomain; exports.AUTHORIZATION_TYPES = AUTHORIZATION_TYPES; exports.attestTypedData = attestTypedData; exports.reattestTypedData = reattestTypedData; exports.updateKeyTypedData = updateKeyTypedData; exports.revokeTypedData = revokeTypedData; exports.setRecordTypedData = setRecordTypedData; exports.authorizationDigest = authorizationDigest; exports.recordKind = recordKind;
1879
+ //# sourceMappingURL=chunk-DC72HPXJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/ben/code/thurinlabs/identity-kit/dist/chunk-DC72HPXJ.cjs","../src/core/abi.ts","../src/core/contract.ts","../src/core/fingerprint.ts","../src/core/pgp.ts","../src/core/efp.ts","../src/core/proofs.ts","../src/core/ensHint.ts","../src/core/authorization.ts"],"names":[],"mappings":"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IAAA;AACL,IAAA;AACG,IAAA;AACtC,EAAA;AACU,EAAA;AACyB,IAAA;AACM,IAAA;AACF,IAAA;AACC,IAAA;AACA,IAAA;AACL,IAAA;AACG,IAAA;AACtC,EAAA;AACW,EAAA;AACwB,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACG,IAAA;AACtC,EAAA;AACQ,EAAA;AAC2B,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACtC,EAAA;AACW,EAAA;AACwB,IAAA;AACA,IAAA;AACD,IAAA;AACD,IAAA;AACE,IAAA;AACG,IAAA;AACtC,EAAA;AACF;AAgBwF;AAC/E,EAAA;AACmC,IAAA;AACI,IAAA;AAC/B,IAAA;AACJ,IAAA;AACE,MAAA;AACoC,MAAA;AACL,MAAA;AACA,MAAA;AAC/B,MAAA;AACG,MAAA;AACd,IAAA;AACF,EAAA;AACF;AAE4F;AACzC,EAAA;AAC1C,EAAA;AACQ,IAAA;AACmC,IAAA;AACnC,IAAA;AAC8B,IAAA;AAC7C,EAAA;AACF;AAE8F;AACrF,EAAA;AACmC,IAAA;AACU,IAAA;AACrC,IAAA;AAC8B,IAAA;AAC7C,EAAA;AACF;AAEwF;AAC/E,EAAA;AACmC,IAAA;AACI,IAAA;AAC/B,IAAA;AACqC,IAAA;AACpD,EAAA;AACF;AAE8F;AACrF,EAAA;AACmC,IAAA;AACU,IAAA;AACrC,IAAA;AACoC,IAAA;AACnD,EAAA;AACF;AAGoM;AAC7J,EAAA;AACvC;AAG8C;AACV,EAAA;AACpC;ARqqDsD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/ben/code/thurinlabs/identity-kit/dist/chunk-DC72HPXJ.cjs","sourcesContent":[null,"// Generated from pgp-registry: `forge inspect PGPRegistry abi --json`. Do not edit by hand.\nexport const REGISTRY_ABI = [\n {\n \"type\": \"function\",\n \"name\": \"ATTEST_TYPEHASH\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"DOMAIN_SEPARATOR\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"EIP712_NAME\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"string\",\n \"internalType\": \"string\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"EIP712_VERSION\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"string\",\n \"internalType\": \"string\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"MAX_KEY_BYTES\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"MAX_RECORD_BYTES\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"MAX_SIG_BYTES\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"MESSAGE_VERSION\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"REATTEST_TYPEHASH\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"REVOKE_TYPEHASH\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"SET_RECORD_TYPEHASH\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"UPDATE_KEY_TYPEHASH\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"VERSION\",\n \"inputs\": [],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"addressesFor\",\n \"inputs\": [\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"address[]\",\n \"internalType\": \"address[]\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"addressesForCount\",\n \"inputs\": [\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"addressesForRange\",\n \"inputs\": [\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"start\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"count\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"out\",\n \"type\": \"address[]\",\n \"internalType\": \"address[]\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"attest\",\n \"inputs\": [\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpSignature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"attestFor\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpSignature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"deadline\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"signature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"attestationCount\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"attestationsOf\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"tuple[]\",\n \"internalType\": \"struct PGPRegistry.Attestation[]\",\n \"components\": [\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"createdAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"revokedAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"messageVersion\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n },\n {\n \"name\": \"keyPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"sigPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ]\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"attestationsOfRange\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"start\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"count\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"out\",\n \"type\": \"tuple[]\",\n \"internalType\": \"struct PGPRegistry.Attestation[]\",\n \"components\": [\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"createdAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"revokedAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"messageVersion\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n },\n {\n \"name\": \"keyPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"sigPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ]\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"cancelAuthorization\",\n \"inputs\": [],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"current\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"found\",\n \"type\": \"bool\",\n \"internalType\": \"bool\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"attestation\",\n \"type\": \"tuple\",\n \"internalType\": \"struct PGPRegistry.Attestation\",\n \"components\": [\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"createdAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"revokedAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"messageVersion\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n },\n {\n \"name\": \"keyPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"sigPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ]\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"fingerprintsForKeyId\",\n \"inputs\": [\n {\n \"name\": \"keyId\",\n \"type\": \"bytes8\",\n \"internalType\": \"bytes8\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes[]\",\n \"internalType\": \"bytes[]\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"fingerprintsForKeyIdCount\",\n \"inputs\": [\n {\n \"name\": \"keyId\",\n \"type\": \"bytes8\",\n \"internalType\": \"bytes8\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"fingerprintsForKeyIdRange\",\n \"inputs\": [\n {\n \"name\": \"keyId\",\n \"type\": \"bytes8\",\n \"internalType\": \"bytes8\"\n },\n {\n \"name\": \"start\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"count\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"out\",\n \"type\": \"bytes[]\",\n \"internalType\": \"bytes[]\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"getAttestation\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"tuple\",\n \"internalType\": \"struct PGPRegistry.Attestation\",\n \"components\": [\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"createdAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"revokedAt\",\n \"type\": \"uint64\",\n \"internalType\": \"uint64\"\n },\n {\n \"name\": \"messageVersion\",\n \"type\": \"uint8\",\n \"internalType\": \"uint8\"\n },\n {\n \"name\": \"keyPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"sigPtr\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ]\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"getPayload\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"pgpSignature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"nonces\",\n \"inputs\": [\n {\n \"name\": \"\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"reattest\",\n \"inputs\": [\n {\n \"name\": \"revokeIndex\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpSignature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"reattestFor\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"revokeIndex\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpSignature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"deadline\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"signature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"record\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"kind\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n }\n ],\n \"outputs\": [\n {\n \"name\": \"\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"stateMutability\": \"view\"\n },\n {\n \"type\": \"function\",\n \"name\": \"revoke\",\n \"inputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"revokeFor\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"deadline\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"signature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"setRecord\",\n \"inputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"kind\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"value\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"setRecordFor\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"kind\",\n \"type\": \"bytes32\",\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"value\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"deadline\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"signature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"updateKey\",\n \"inputs\": [\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"function\",\n \"name\": \"updateKeyFor\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"pgpPublicKey\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"deadline\",\n \"type\": \"uint256\",\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"signature\",\n \"type\": \"bytes\",\n \"internalType\": \"bytes\"\n }\n ],\n \"outputs\": [],\n \"stateMutability\": \"nonpayable\"\n },\n {\n \"type\": \"event\",\n \"name\": \"Attested\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"indexed\": true,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"indexed\": true,\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"indexed\": true,\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"fingerprint\",\n \"type\": \"bytes\",\n \"indexed\": false,\n \"internalType\": \"bytes\"\n },\n {\n \"name\": \"messageVersion\",\n \"type\": \"uint8\",\n \"indexed\": false,\n \"internalType\": \"uint8\"\n },\n {\n \"name\": \"keyPtr\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"sigPtr\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"submitter\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n }\n ],\n \"anonymous\": false\n },\n {\n \"type\": \"event\",\n \"name\": \"KeyUpdated\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"indexed\": true,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"indexed\": true,\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"indexed\": true,\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"oldKeyPtr\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"newKeyPtr\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"submitter\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n }\n ],\n \"anonymous\": false\n },\n {\n \"type\": \"event\",\n \"name\": \"NonceUsed\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"indexed\": true,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"nonce\",\n \"type\": \"uint256\",\n \"indexed\": false,\n \"internalType\": \"uint256\"\n }\n ],\n \"anonymous\": false\n },\n {\n \"type\": \"event\",\n \"name\": \"RecordSet\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"indexed\": true,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"indexed\": true,\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"kind\",\n \"type\": \"bytes32\",\n \"indexed\": true,\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"submitter\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n }\n ],\n \"anonymous\": false\n },\n {\n \"type\": \"event\",\n \"name\": \"Revoked\",\n \"inputs\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"indexed\": true,\n \"internalType\": \"address\"\n },\n {\n \"name\": \"fingerprintHash\",\n \"type\": \"bytes32\",\n \"indexed\": true,\n \"internalType\": \"bytes32\"\n },\n {\n \"name\": \"index\",\n \"type\": \"uint256\",\n \"indexed\": true,\n \"internalType\": \"uint256\"\n },\n {\n \"name\": \"submitter\",\n \"type\": \"address\",\n \"indexed\": false,\n \"internalType\": \"address\"\n }\n ],\n \"anonymous\": false\n },\n {\n \"type\": \"error\",\n \"name\": \"AlreadyRevoked\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"AuthorizationExpired\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"DeploymentFailed\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"DuplicateActiveFingerprint\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"EmptyPublicKey\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"EmptySignature\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"IndexOutOfBounds\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"InvalidAuthorization\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"InvalidFingerprintLength\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"InvalidPointer\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"PublicKeyTooLarge\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"RecordTooLarge\",\n \"inputs\": []\n },\n {\n \"type\": \"error\",\n \"name\": \"SignatureTooLarge\",\n \"inputs\": []\n }\n] as const\n","import { mainnet, sepolia, foundry } from 'viem/chains'\nexport { REGISTRY_ABI } from './abi'\n\n/**\n * PGPRegistry v2 is deployed with CREATE2, so it has the same address on every\n * network. (v1 lived at 0xf7a45BC662A78a6fb417ED5f52b3766cbf13EbBb on mainnet and\n * is no longer read by Thurin.)\n */\nexport const REGISTRY_ADDRESS = '0x9302E02e2869e129aC8516fE5eFFd51EA3082c09' as const\n\n/** Networks the PGPRegistry is deployed on. `mainnet` is the default everywhere. */\nexport type NetworkName = 'mainnet' | 'sepolia' | 'local'\n\nexport interface RegistryDeployment {\n chainId: number\n address: `0x${string}`\n /** Block the registry was deployed in (informational; v2 needs no log scans). */\n deployBlock: bigint\n /** Block explorer base URL for address/tx links. Empty when there is none. */\n explorerUrl: string\n /** Keyless public RPC used when no rpcUrl is configured. */\n defaultRpcUrl: string\n}\n\nexport const NETWORKS: Record<NetworkName, RegistryDeployment> = {\n mainnet: {\n chainId: 1,\n address: REGISTRY_ADDRESS,\n deployBlock: 25962908n, // 2026-09-12, tx 0xda4c3a19cae4e71ffb02559a048403f664d5a7967c19d42158c39600ec63bad8\n explorerUrl: 'https://etherscan.io',\n defaultRpcUrl: 'https://ethereum.publicnode.com', // same service as ethereum-rpc.publicnode.com; this hostname resolves on more networks\n },\n sepolia: {\n chainId: 11155111,\n address: REGISTRY_ADDRESS,\n deployBlock: 11683667n, // 2026-09-11, tx 0x3e40e8f5b9fbf82a4b8943561bcc9b964b2987de10640ac8ff3a1b4ef0725322\n explorerUrl: 'https://sepolia.etherscan.io',\n defaultRpcUrl: 'https://ethereum-sepolia.publicnode.com',\n },\n /** A local anvil instance (`anvil`, chain id 31337). Deploy with pgp-registry's Deploy script. */\n local: {\n chainId: 31337,\n address: REGISTRY_ADDRESS,\n deployBlock: 0n,\n explorerUrl: '',\n defaultRpcUrl: 'http://127.0.0.1:8545',\n },\n}\n\nexport function isNetworkName(value: unknown): value is NetworkName {\n return value === 'mainnet' || value === 'sepolia' || value === 'local'\n}\n\n/**\n * The registry deployment for a network. `addressOverride` swaps the contract\n * address (e.g. a local deploy that landed somewhere else).\n */\nexport function getRegistry(network: NetworkName = 'mainnet', addressOverride?: string): RegistryDeployment {\n const r = NETWORKS[network]\n if (!r) throw new Error(`Unknown network: ${network}`)\n if (addressOverride && /^0x[0-9a-fA-F]{40}$/.test(addressOverride)) {\n return { ...r, address: addressOverride as `0x${string}` }\n }\n return r\n}\n\n/** The viem chain object for a network name. */\nexport function chainFor(network: NetworkName = 'mainnet') {\n if (network === 'sepolia') return sepolia\n if (network === 'local') return foundry\n return mainnet\n}\n","import { keccak256, type Hex } from 'viem'\n\n/** 40 hex chars (OpenPGP v4) or 64 hex chars (v6), spaces/0x tolerated. */\nconst FINGERPRINT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/\n\n/** Normalize a fingerprint to lowercase hex with no prefix or spaces. Returns null if malformed. */\nexport function normalizeFingerprint(input: string): string | null {\n const hex = input.replace(/^0x/i, '').replace(/\\s+/g, '').toLowerCase()\n return FINGERPRINT_HEX.test(hex) ? hex : null\n}\n\n/** Fingerprint → the raw `bytes` argument the v2 registry takes (0x-prefixed, 20 or 32 bytes). */\nexport function fingerprintToBytes(fingerprint: string): Hex {\n const hex = normalizeFingerprint(fingerprint)\n if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`)\n return `0x${hex}`\n}\n\n/** Raw `bytes` from the registry → lowercase hex fingerprint without 0x. */\nexport function bytesToFingerprint(bytes: Hex | string): string {\n return bytes.replace(/^0x/i, '').toLowerCase()\n}\n\n/** keccak256 of the raw fingerprint bytes — the `fingerprintHash` the registry indexes by. */\nexport function fingerprintHash(fingerprint: string): Hex {\n return keccak256(fingerprintToBytes(fingerprint))\n}\n\n/**\n * The long key ID as the `bytes8` the registry indexes by. Per RFC 9580 §5.5.4 it is the\n * low-order 8 bytes of a v4 (20-byte) fingerprint and the high-order 8 bytes of a v6\n * (32-byte) fingerprint — the same value `gpg --keyid-format long` prints.\n */\nexport function keyIdOf(fingerprint: string): Hex {\n const hex = normalizeFingerprint(fingerprint)\n if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`)\n return `0x${keyIdHex(hex)}`\n}\n\n/** Long key ID (16 lowercase hex chars) of a normalized fingerprint. */\nexport function keyIdHex(normalizedFingerprint: string): string {\n return normalizedFingerprint.length === 64 ? normalizedFingerprint.slice(0, 16) : normalizedFingerprint.slice(-16)\n}\n\n/** A 16-hex long key ID (spaces/0x tolerated) → `bytes8`. */\nexport function keyIdToBytes(keyId: string): Hex | null {\n const hex = keyId.replace(/^0x/i, '').replace(/\\s+/g, '').toLowerCase()\n return /^[0-9a-f]{16}$/.test(hex) ? `0x${hex}` : null\n}\n","import type { PGPKeyInfo, PGPVerification } from './types'\n\n/**\n * openpgp.js refuses secp256k1 by default because RFC 9580 does not list the\n * curve — a compatibility choice, not a security one. A secp256k1 PGP key is a\n * valid key (and, incidentally, doubles as an Ethereum key: the address falls out\n * of the same public point). Thurin verifies signatures over any curve openpgp.js\n * can compute; whether a holder reuses such a key as a wallet is their business.\n * In Node, openpgp.js needs the optional `eckey-utils` package for this curve;\n * the browser build does not.\n *\n * The low-level packet/key methods take a *complete* config (they index into\n * it), so this builds one from the library's defaults rather than passing a\n * partial object.\n */\nfunction pgpConfig(openpgp: typeof import('openpgp')) {\n return { ...openpgp.config, rejectCurves: new Set() as Set<never> }\n}\n\n/** Human-readable algorithm for a key or subkey packet: \"Ed25519\", \"Cv25519\", \"RSA 4096\", \"NIST P-256\", … */\nfunction algorithmName(keyOrSubkey: any): string {\n try {\n const info = keyOrSubkey.getAlgorithmInfo() as { algorithm: string; bits?: number; curve?: string }\n const curve = (info.curve || '').toLowerCase()\n if (curve.startsWith('ed25519') || info.algorithm === 'ed25519' || info.algorithm === 'eddsaLegacy') return 'Ed25519'\n if (curve.startsWith('curve25519') || info.algorithm === 'x25519') return 'Cv25519'\n if (curve.startsWith('ed448')) return 'Ed448'\n if (curve.startsWith('curve448') || info.algorithm === 'x448') return 'X448'\n if (curve.startsWith('nistp') || curve.startsWith('p')) return `NIST P-${curve.replace(/\\D/g, '')}`\n if (curve.startsWith('secp')) return curve\n if (curve.startsWith('brainpool')) return curve\n if (info.algorithm.startsWith('rsa')) return info.bits ? `RSA ${info.bits}` : 'RSA'\n if (info.algorithm.startsWith('dsa')) return info.bits ? `DSA ${info.bits}` : 'DSA'\n if (info.algorithm.startsWith('elgamal')) return info.bits ? `ElGamal ${info.bits}` : 'ElGamal'\n return info.curve || info.algorithm\n } catch {\n return String(keyOrSubkey.keyPacket?.algorithm ?? 'unknown')\n }\n}\n\nexport async function parsePgpKey(armoredKey: string): Promise<PGPKeyInfo | null> {\n try {\n const openpgp = await import('openpgp')\n const { readKey } = openpgp\n const config = pgpConfig(openpgp)\n const key = await readKey({ armoredKey })\n const fingerprint = key.getFingerprint().toUpperCase()\n const algorithm = algorithmName(key)\n const created = key.keyPacket.created?.toISOString() ?? null\n const expiration = await key.getExpirationTime()\n const expires =\n expiration && expiration !== Infinity\n ? new Date(expiration as number).toISOString()\n : null\n\n // openpgp does not verify certification signatures when a key is parsed —\n // it groups them by issuer key ID only. A key's fingerprint also covers\n // only the primary key packet, not its user IDs or their certifications.\n // Each self-certification is therefore verified against the primary key\n // here before its user ID and notations are treated as authoritative.\n const userIDs: string[] = []\n const notations: { name: string; value: string }[] = []\n const seen = new Set<string>()\n for (const user of key.users) {\n const certs = (user as any).selfCertifications\n if (!certs || certs.length === 0) continue\n\n let userVerified = false\n for (const cert of certs) {\n try {\n await cert.verify(\n key.keyPacket,\n cert.signatureType,\n { userID: (user as any).userID, key: key.keyPacket },\n undefined, // date: now\n undefined, // detached\n config,\n )\n } catch {\n continue // signature not made by this key — ignore this certification\n }\n userVerified = true\n\n if (cert.rawNotations) {\n for (const n of cert.rawNotations) {\n const name =\n typeof n.name === 'string' ? n.name : new TextDecoder().decode(n.name)\n const value =\n n.value instanceof Uint8Array\n ? new TextDecoder().decode(n.value)\n : typeof n.value === 'string'\n ? n.value\n : null\n if (value) {\n // Dedupe by value alone: the same proof target is often present\n // under both proof@thurin.id and proof@ariadne.id, and should\n // surface once, not once per namespace.\n if (!seen.has(value)) {\n seen.add(value)\n notations.push({ name, value })\n }\n }\n }\n }\n }\n\n if (userVerified) {\n const uid = (user as any).userID?.userID\n if (uid) userIDs.push(uid)\n }\n }\n\n const subkeys = key.subkeys.map((sk: any) => ({\n algorithm: algorithmName(sk),\n created: sk.keyPacket.created?.toISOString() ?? null,\n fingerprint: sk.getFingerprint().toUpperCase(),\n }))\n\n return { fingerprint, userIDs, algorithm, created, expires, notations, subkeys }\n } catch {\n return null\n }\n}\n\nexport async function verifyAttestation({\n pgpPublicKey,\n pgpSignature,\n fingerprint,\n ethAddress,\n}: {\n pgpPublicKey: string | null\n pgpSignature: string | null\n fingerprint: string\n ethAddress: string\n}): Promise<PGPVerification> {\n try {\n if (!pgpPublicKey || !pgpSignature) {\n return { verified: false, reason: 'Missing PGP data' }\n }\n\n const openpgp = await import('openpgp')\n const { readKey, readCleartextMessage, LiteralDataPacket } = openpgp\n const config = pgpConfig(openpgp)\n\n const publicKey = await readKey({ armoredKey: pgpPublicKey })\n const keyFingerprint = publicKey.getFingerprint().toUpperCase()\n if (keyFingerprint !== fingerprint.toUpperCase()) {\n return { verified: false, reason: 'Key fingerprint mismatch' }\n }\n\n const message = await readCleartextMessage({ cleartextMessage: pgpSignature })\n\n // Verify the way gpg does: the key (or subkey) must be valid *now* — bound to\n // this primary, unrevoked, unexpired — and the signature must be sound. We\n // deliberately do not ask openpgp.verify(), which also demands the key was\n // valid at the instant the signature was made. Thurin's update flow tells\n // people to `export-minimal` after editing notations, which keeps only the\n // newest self-certification; when that postdates the attest signature\n // (Ben's own key, 2026-09-14: notations edited 16 minutes after signing),\n // openpgp.verify() reports \"Could not find valid self-signature\" for a\n // signature gpg verifies fine. A key that has since been revoked or expired\n // still fails here, as it should.\n // These are internals the .d.ts does not expose (the signature packet list,\n // the CRLF-normalised text, LiteralDataPacket.setText); they are stable across\n // openpgp.js 6 and exercised by the real-data tests in attestation.test.ts.\n const msg = message as any\n const now = new Date()\n let signed = false\n for (const sig of msg.signature.packets) {\n if (!publicKey.getKeys(sig.issuerKeyID).length) continue\n const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, undefined, config)\n const literal: any = new LiteralDataPacket()\n literal.setText(msg.text ?? message.getText().replace(/\\r?\\n/g, '\\r\\n'))\n await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config)\n signed = true\n break\n }\n if (!signed) return { verified: false, reason: 'Message is not signed by this key' }\n\n const signedText = message.getText()\n if (!signedText.toLowerCase().includes(ethAddress.toLowerCase())) {\n return { verified: false, reason: 'Signed message does not contain ETH address' }\n }\n\n return { verified: true }\n } catch {\n return { verified: false, reason: 'Signature verification failed' }\n }\n}\n\nexport interface StrippedKey {\n /** Armored public key containing only the non-email user IDs. */\n armored: string\n /** User IDs kept (no `@`). */\n kept: string[]\n /** User IDs removed (contained an `@`). */\n removed: string[]\n}\n\nconst EMAIL_UID = /@/\n\n/**\n * Remove every user ID that contains an email address, keeping the rest (and\n * their self-certifications, so proof notations on a non-email user ID survive).\n * Returns null when nothing would remain — openpgp needs at least one\n * self-certified user ID to verify signatures, so such a key must not be\n * published. The key material and subkeys are untouched; the fingerprint is\n * unchanged.\n */\nexport async function stripEmailUserIDs(armoredKey: string): Promise<StrippedKey | null> {\n try {\n const { readKey } = await import('openpgp')\n const key = await readKey({ armoredKey })\n const kept: string[] = []\n const removed: string[] = []\n const users = (key as any).users.filter((u: any) => {\n const uid: string | undefined = u.userID?.userID\n if (!uid) return false\n if (EMAIL_UID.test(uid)) { removed.push(uid); return false }\n kept.push(uid)\n return true\n })\n if (users.length === 0) return null\n ;(key as any).users = users\n return { armored: key.armor(), kept, removed }\n } catch {\n return null\n }\n}\n\n/** True when any user ID on the key contains an email address. */\nexport async function hasEmailUserID(armoredKey: string): Promise<boolean> {\n try {\n const { readKey } = await import('openpgp')\n const key = await readKey({ armoredKey })\n return (key as any).users.some((u: any) => EMAIL_UID.test(u.userID?.userID ?? ''))\n } catch {\n return false\n }\n}\n","import type { EFPGraph } from './types'\n\nconst EFP_API = 'https://api.ethfollow.xyz/api/v1'\n\n/**\n * EFP social graph for an address. Followers are counted by EFP for any address,\n * whether or not it has published a list of its own; following and top-8 exist\n * only when it has (its \"primary list\"). An address with followers but no list\n * — a project account that has never followed anyone — is still on EFP.\n */\nexport async function fetchEFPGraph(address: string): Promise<EFPGraph | null> {\n try {\n const [statsResp, listsResp] = await Promise.all([\n fetch(`${EFP_API}/users/${address}/stats`),\n fetch(`${EFP_API}/users/${address}/lists`),\n ])\n if (!statsResp.ok && !listsResp.ok) return null\n\n const statsData = statsResp.ok ? await statsResp.json() : {}\n const listsData = listsResp.ok ? await listsResp.json() : {}\n const followers = parseInt(statsData.followers_count) || 0\n const listId = listsData.primary_list\n\n let following = 0\n let top8: string[] = []\n if (listId) {\n const [followingResp, top8Resp] = await Promise.all([\n fetch(`${EFP_API}/lists/${listId}/following?limit=100`),\n fetch(`${EFP_API}/lists/${listId}/following?limit=8&tags=top8`),\n ])\n const followingData = followingResp.ok ? await followingResp.json() : { following: [] }\n const top8Data = top8Resp.ok ? await top8Resp.json() : { following: [] }\n following = (followingData.following || []).length\n top8 = (top8Data.following || []).map((f: any) => f.data)\n }\n\n return { followers, following, top8, hasEfp: !!listId || followers > 0 }\n } catch {\n return null\n }\n}\n","import { keyIdHex } from './fingerprint'\nimport type { Notation, Proof, PGPVerification } from './types'\n\nconst PROVIDERS: {\n provider: string\n label: string\n pattern: RegExp\n parse: (m: RegExpMatchArray) => Record<string, string>\n}[] = [\n {\n provider: 'github',\n label: 'GitHub',\n pattern: /^https:\\/\\/gist\\.github\\.com\\/([^/]+)\\/([a-f0-9]+)$/i,\n parse: (m) => ({ user: m[1], gistId: m[2] }),\n },\n {\n // A repository's description — the form an organisation can use, since\n // gists belong to user accounts only. Same shape as the Codeberg proof.\n provider: 'github',\n label: 'GitHub',\n pattern: /^https:\\/\\/github\\.com\\/([A-Za-z0-9-]+)\\/([A-Za-z0-9._-]+)$/i,\n parse: (m) => ({ user: m[1], repo: m[2] }),\n },\n {\n provider: 'dns',\n label: 'DNS',\n // domain is restricted to a hostname (labels may include underscores for\n // records like _thurin.example.com); rejects slashes, userinfo, and spaces.\n pattern: /^dns:([a-z0-9._-]+)\\?type=TXT$/i,\n parse: (m) => ({ domain: m[1] }),\n },\n {\n provider: 'farcaster',\n label: 'Farcaster',\n pattern: /^https:\\/\\/farcaster\\.xyz\\/([^/]+)\\/(0x[a-f0-9]+)$/i,\n parse: (m) => ({ user: m[1], castHash: m[2] }),\n },\n {\n provider: 'codeberg',\n label: 'Codeberg',\n pattern: /^https:\\/\\/codeberg\\.org\\/([^/]+)\\/([^/]+)$/i,\n parse: (m) => ({ user: m[1], repo: m[2] }),\n },\n {\n provider: 'mastodon',\n label: 'Mastodon',\n // instance is restricted to a bare hostname (optional port) so a value\n // like \"mastodon.social@evil.com\" can't redirect the lookup elsewhere.\n pattern: /^https:\\/\\/([a-z0-9.-]+(?::\\d+)?)\\/@([^/@?#\\s]+)$/i,\n parse: (m) => ({ instance: m[1], user: m[2] }),\n },\n]\n\nexport function identifyProof(notation: Notation): Proof | null {\n if (notation.name !== 'proof@thurin.id') return null\n\n for (const { provider, label, pattern, parse } of PROVIDERS) {\n const m = notation.value.match(pattern)\n if (m) {\n return { provider, label, url: notation.value, ...parse(m) }\n }\n }\n\n return { provider: 'unknown', label: 'Unknown', url: notation.value }\n}\n\nexport function displayUrl(proof: Proof): string {\n if (proof.provider === 'dns') return proof.domain!\n if (proof.provider === 'github') return proof.user!\n if (proof.provider === 'farcaster') return `@${proof.user}`\n if (proof.provider === 'codeberg') return proof.user!\n if (proof.provider === 'mastodon') return `@${proof.user}@${proof.instance}`\n return proof.url\n}\n\nexport function proofHref(proof: Proof): string | null {\n if (proof.provider === 'dns') return `https://${proof.domain}`\n if (proof.provider === 'github') return `https://github.com/${proof.user}`\n if (proof.provider === 'farcaster') return `https://farcaster.xyz/${proof.user}`\n if (proof.provider === 'codeberg') return `https://codeberg.org/${proof.user}`\n if (proof.provider === 'mastodon') return `https://${proof.instance}/@${proof.user}`\n if (proof.url.startsWith('http')) return proof.url\n return null\n}\n\nexport function proofSecondaryHref(proof: Proof): string | null {\n if (proof.provider === 'github') return proof.url\n if (proof.provider === 'farcaster') return proof.url\n if (proof.provider === 'codeberg') return proof.url\n if (proof.provider === 'mastodon') return proof.url\n return null\n}\n\n// ─── Verification ──────────────────────────────────────────────────────────\n\nconst FPR_TOKEN = 'OPENPGP4FPR:'\n\nfunction containsFingerprint(text: string, fingerprint: string): boolean {\n return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase())\n}\n\nfunction containsFingerprintUrl(text: string, fingerprint: string): boolean {\n return text.toUpperCase().includes(fingerprint.toUpperCase())\n}\n\nasync function verifyGitHub(proof: Proof, fingerprint: string): Promise<PGPVerification> {\n if (proof.repo) return verifyGitHubRepo(proof, fingerprint)\n try {\n const resp = await fetch(`https://api.github.com/gists/${proof.gistId}`)\n if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` }\n const data = await resp.json()\n\n // A gist is identified solely by its globally-unique ID; the username in\n // the URL is not authoritative. Confirm the gist owner matches the claimed\n // account before trusting the token it contains.\n if (data.owner?.login?.toLowerCase() !== proof.user!.toLowerCase()) {\n return { verified: false, reason: 'Gist owner does not match claimed user' }\n }\n\n for (const file of Object.values(data.files || {}) as any[]) {\n if (file.content && containsFingerprint(file.content, fingerprint)) {\n return { verified: true }\n }\n }\n return { verified: false, reason: 'Fingerprint token not found in gist' }\n } catch (err: any) {\n return { verified: false, reason: `GitHub fetch failed: ${err.message}` }\n }\n}\n\nasync function verifyGitHubRepo(proof: Proof, fingerprint: string): Promise<PGPVerification> {\n try {\n const resp = await fetch(\n `https://api.github.com/repos/${encodeURIComponent(proof.user!)}/${encodeURIComponent(proof.repo!)}`,\n )\n if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` }\n const data = await resp.json()\n\n // GitHub follows renames and redirects, so the API may answer for a repo\n // under a different owner than the URL names. Only trust it if the owner\n // is the account being claimed.\n if (data.owner?.login?.toLowerCase() !== proof.user!.toLowerCase()) {\n return { verified: false, reason: 'Repository owner does not match claimed account' }\n }\n\n if (data.description && containsFingerprint(data.description, fingerprint)) {\n return { verified: true }\n }\n return { verified: false, reason: 'Fingerprint token not found in repository description' }\n } catch (err: any) {\n return { verified: false, reason: `GitHub fetch failed: ${err.message}` }\n }\n}\n\nasync function verifyDNS(proof: Proof, fingerprint: string): Promise<PGPVerification> {\n try {\n const resp = await fetch(\n `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain!)}&type=TXT`,\n { headers: { Accept: 'application/dns-json' } },\n )\n if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` }\n const data = await resp.json()\n\n for (const answer of data.Answer || []) {\n // TXT records only (type 16) — a CNAME or other record in the resolution\n // chain must not be eligible to match the token.\n if (answer.type !== 16) continue\n if (answer.data && containsFingerprint(answer.data, fingerprint)) {\n return { verified: true }\n }\n }\n return { verified: false, reason: 'Fingerprint token not found in TXT records' }\n } catch (err: any) {\n return { verified: false, reason: `DNS fetch failed: ${err.message}` }\n }\n}\n\nconst NEYNAR_HUB = 'https://hub-api.neynar.com'\n\nasync function resolveFid(\n username: string,\n apiKey: string,\n): Promise<number | null> {\n const resp = await fetch(\n `${NEYNAR_HUB}/v1/userNameProofByName?name=${encodeURIComponent(username)}`,\n { headers: { 'x-api-key': apiKey } },\n )\n if (!resp.ok) return null\n const data = await resp.json()\n return data.fid ?? null\n}\n\nasync function verifyFarcaster(\n proof: Proof,\n fingerprint: string,\n neynarApiKey?: string,\n): Promise<PGPVerification> {\n if (!neynarApiKey) {\n return { verified: false, reason: 'Farcaster verification requires a Neynar API key' }\n }\n\n try {\n const fid = await resolveFid(proof.user!, neynarApiKey)\n if (!fid) return { verified: false, reason: `Could not resolve Farcaster user \"${proof.user}\"` }\n\n const headers = { 'x-api-key': neynarApiKey }\n let pageToken = ''\n for (let page = 0; page < 5; page++) {\n const url = `${NEYNAR_HUB}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ''}`\n const resp = await fetch(url, { headers })\n if (!resp.ok) return { verified: false, reason: `Farcaster Hub returned ${resp.status}` }\n const data = await resp.json()\n\n for (const msg of data.messages || []) {\n if (msg.hash && msg.hash.startsWith(proof.castHash!)) {\n const text = msg.data?.castAddBody?.text || ''\n if (containsFingerprint(text, fingerprint)) {\n return { verified: true }\n }\n // A short cast-hash prefix can match multiple casts; keep scanning\n // rather than stopping at the first prefix match without the token.\n }\n }\n\n if (!data.nextPageToken) break\n pageToken = data.nextPageToken\n }\n\n return { verified: false, reason: 'Cast not found' }\n } catch (err: any) {\n return { verified: false, reason: `Farcaster fetch failed: ${err.message}` }\n }\n}\n\nasync function verifyCodeberg(proof: Proof, fingerprint: string): Promise<PGPVerification> {\n try {\n const resp = await fetch(\n `https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user!)}/${encodeURIComponent(proof.repo!)}`,\n )\n if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` }\n const data = await resp.json()\n\n if (data.description && containsFingerprint(data.description, fingerprint)) {\n return { verified: true }\n }\n\n return { verified: false, reason: 'Fingerprint token not found in repo description' }\n } catch (err: any) {\n return { verified: false, reason: `Codeberg fetch failed: ${err.message}` }\n }\n}\n\nasync function verifyMastodon(proof: Proof, fingerprint: string): Promise<PGPVerification> {\n try {\n // The instance host is user-supplied and interpolated into the request URL,\n // so restrict it to a bare hostname (optional port) before use — otherwise a\n // value such as \"mastodon.social@evil.com\" would send the lookup to another\n // server while still displaying as a legitimate handle.\n if (!proof.instance || !/^[a-z0-9.-]+(?::\\d+)?$/i.test(proof.instance)) {\n return { verified: false, reason: 'Invalid Mastodon instance host' }\n }\n const resp = await fetch(\n `https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user!)}`,\n )\n if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` }\n const data = await resp.json()\n\n const keyId = keyIdHex(fingerprint.replace(/^0x/i, '').replace(/\\s+/g, '').toLowerCase())\n\n for (const field of data.fields || []) {\n const text = field.value.replace(/<[^>]*>/g, '')\n if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {\n return { verified: true }\n }\n }\n\n if (data.note) {\n const text = data.note.replace(/<[^>]*>/g, '')\n if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {\n return { verified: true }\n }\n }\n\n return { verified: false, reason: 'Fingerprint not found in profile fields or bio' }\n } catch (err: any) {\n return { verified: false, reason: `Mastodon fetch failed: ${err.message}` }\n }\n}\n\ntype Verifier = (proof: Proof, fingerprint: string, neynarApiKey?: string) => Promise<PGPVerification>\n\nconst verifiers: Record<string, Verifier> = {\n github: verifyGitHub,\n dns: verifyDNS,\n farcaster: verifyFarcaster,\n codeberg: verifyCodeberg,\n mastodon: verifyMastodon,\n}\n\nexport async function verifyProof(\n proof: Proof,\n fingerprint: string,\n neynarApiKey?: string,\n): Promise<PGPVerification> {\n const fn = verifiers[proof.provider]\n if (!fn) return { verified: false, reason: 'Unknown provider' }\n return fn(proof, fingerprint, neynarApiKey)\n}\n","import { namehash, normalize } from 'viem/ens'\nimport { normalizeFingerprint } from './fingerprint'\n\n/**\n * The `id.thurin` ENS text record: a name points at the PGP key claimed on the Thurin\n * registry by the address the name resolves to. It is a discovery hint, not part of the\n * trust chain — the name owner set the address, the address signed the claim, so a reader\n * verifies by resolving the name and reading the registry. The record only says where to\n * look. `id.thurin` is a service key under ENSIP-5 (reverse-dot of thurin.id); ENSIP-18\n * says service-key values are bare, so the value is the 40-hex fingerprint, uppercase.\n */\nexport const ENS_HINT_KEY = 'id.thurin' as const\n\nexport type EnsHintState = 'match' | 'unset' | 'mismatch'\n\nexport interface EnsHint {\n key: typeof ENS_HINT_KEY\n state: EnsHintState\n /** The raw record value, or null when the name carries no record. */\n record: string | null\n /** The record as a lowercase fingerprint, or null when it does not parse as one. */\n fingerprint: string | null\n /** The verified claim's fingerprint (lowercase) the record is compared against, if any. */\n expected: string | null\n /** Why a record does not match; absent for `match` and `unset`. */\n reason?: string\n}\n\n/**\n * Compare a name's `id.thurin` record with the fingerprint the registry verifies for the\n * name's address. Parsing is lenient (case, spaces, 0x); what Thurin writes is strict.\n */\nexport function ensHintFor(record: string | null | undefined, verifiedFingerprint: string | null | undefined): EnsHint {\n const raw = record?.trim() ?? ''\n const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null\n if (!raw) return { key: ENS_HINT_KEY, state: 'unset', record: null, fingerprint: null, expected }\n const fingerprint = normalizeFingerprint(raw)\n if (!fingerprint) return { key: ENS_HINT_KEY, state: 'mismatch', record: raw, fingerprint: null, expected, reason: 'The record is not a PGP fingerprint' }\n if (!expected) return { key: ENS_HINT_KEY, state: 'mismatch', record: raw, fingerprint, expected, reason: 'The address has no verified claim' }\n if (fingerprint === expected) return { key: ENS_HINT_KEY, state: 'match', record: raw, fingerprint, expected }\n return { key: ENS_HINT_KEY, state: 'mismatch', record: raw, fingerprint, expected, reason: 'The record names a key this address has not claimed' }\n}\n\n/** The value Thurin writes: the fingerprint, uppercase, bare. */\nexport function ensHintValue(fingerprint: string): string {\n const fp = normalizeFingerprint(fingerprint)\n if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`)\n return fp.toUpperCase()\n}\n\n/** The `text` / `setText` pair every ENS resolver speaks (ENSIP-5); the same on ENSv2. */\nexport const ENS_TEXT_RESOLVER_ABI = [\n { type: 'function', name: 'text', stateMutability: 'view', inputs: [{ name: 'node', type: 'bytes32' }, { name: 'key', type: 'string' }], outputs: [{ name: '', type: 'string' }] },\n { type: 'function', name: 'setText', stateMutability: 'nonpayable', inputs: [{ name: 'node', type: 'bytes32' }, { name: 'key', type: 'string' }, { name: 'value', type: 'string' }], outputs: [] },\n] as const\n\n/**\n * The `setText` call that links a name to a claim. The resolver address is not part of it\n * on purpose: look it up at write time (`client.getEnsResolver({ name })`) — on ENSv2 every\n * account has its own resolver, so a cached or hardcoded one is the wrong contract.\n */\nexport function ensHintWrite(name: string, fingerprint: string) {\n const normalized = normalize(name)\n return {\n abi: ENS_TEXT_RESOLVER_ABI,\n functionName: 'setText' as const,\n args: [namehash(normalized), ENS_HINT_KEY, ensHintValue(fingerprint)] as const,\n name: normalized,\n }\n}\n\n/** Minimal client shape: viem's PublicClient has both. */\nexport interface EnsTextReader {\n getEnsText(args: { name: string; key: string }): Promise<string | null>\n}\n\n/** Read a name's `id.thurin` record and compare it with the verified fingerprint. */\nexport async function fetchEnsHint(client: EnsTextReader, name: string, verifiedFingerprint: string | null | undefined): Promise<EnsHint> {\n const record = await client.getEnsText({ name: normalize(name), key: ENS_HINT_KEY })\n return ensHintFor(record, verifiedFingerprint)\n}\n","import { hashTypedData, keccak256, stringToHex, type Hex, type TypedDataDomain } from 'viem'\nimport { fingerprintToBytes } from './fingerprint'\n\n/**\n * EIP-712 authorizations for the registry's relayed writes (`attestFor`, `reattestFor`,\n * `updateKeyFor`, `revokeFor`, `setRecordFor`). The owner signs one of these typed\n * structs; anyone can then submit the matching call and pay the gas. Every field is\n * bound, plus the owner's current `nonce` (read with `nonces(owner)`) and a `deadline`.\n *\n * The domain includes the chain id, so an authorization for one network cannot be\n * replayed on another even though the registry has the same address everywhere.\n */\nexport const EIP712_NAME = 'Thurin PGPRegistry'\nexport const EIP712_VERSION = '2'\n\nexport function registryDomain(chainId: number, verifyingContract: Hex): TypedDataDomain {\n return { name: EIP712_NAME, version: EIP712_VERSION, chainId, verifyingContract }\n}\n\nexport const AUTHORIZATION_TYPES = {\n Attest: [\n { name: 'owner', type: 'address' },\n { name: 'fingerprint', type: 'bytes' },\n { name: 'pgpSignature', type: 'bytes' },\n { name: 'pgpPublicKey', type: 'bytes' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n Reattest: [\n { name: 'owner', type: 'address' },\n { name: 'revokeIndex', type: 'uint256' },\n { name: 'fingerprint', type: 'bytes' },\n { name: 'pgpSignature', type: 'bytes' },\n { name: 'pgpPublicKey', type: 'bytes' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n UpdateKey: [\n { name: 'owner', type: 'address' },\n { name: 'index', type: 'uint256' },\n { name: 'pgpPublicKey', type: 'bytes' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n Revoke: [\n { name: 'owner', type: 'address' },\n { name: 'index', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n SetRecord: [\n { name: 'owner', type: 'address' },\n { name: 'index', type: 'uint256' },\n { name: 'kind', type: 'bytes32' },\n { name: 'value', type: 'bytes' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n} as const\n\nexport type AuthorizationAction = keyof typeof AUTHORIZATION_TYPES\n\ninterface Common { owner: Hex; nonce: bigint; deadline: bigint }\nexport type AttestAuthorization = Common & { fingerprint: string; pgpSignature: string; pgpPublicKey: string }\nexport type ReattestAuthorization = AttestAuthorization & { revokeIndex: bigint }\nexport type UpdateKeyAuthorization = Common & { index: bigint; pgpPublicKey: string }\nexport type RevokeAuthorization = Common & { index: bigint }\nexport type SetRecordAuthorization = Common & { index: bigint; kind: Hex; value: Hex }\n\n/**\n * Build the object to pass to viem/wagmi `signTypedData`. `fingerprint` is the hex\n * fingerprint; `pgpSignature` / `pgpPublicKey` are the armored text exactly as they\n * will be submitted (they are hashed as UTF-8 bytes).\n */\nexport function attestTypedData(chainId: number, registry: Hex, a: AttestAuthorization) {\n return {\n domain: registryDomain(chainId, registry),\n types: { Attest: AUTHORIZATION_TYPES.Attest },\n primaryType: 'Attest' as const,\n message: {\n owner: a.owner,\n fingerprint: fingerprintToBytes(a.fingerprint),\n pgpSignature: stringToHex(a.pgpSignature),\n pgpPublicKey: stringToHex(a.pgpPublicKey),\n nonce: a.nonce,\n deadline: a.deadline,\n },\n }\n}\n\nexport function reattestTypedData(chainId: number, registry: Hex, a: ReattestAuthorization) {\n const base = attestTypedData(chainId, registry, a)\n return {\n domain: base.domain,\n types: { Reattest: AUTHORIZATION_TYPES.Reattest },\n primaryType: 'Reattest' as const,\n message: { ...base.message, revokeIndex: a.revokeIndex },\n }\n}\n\nexport function updateKeyTypedData(chainId: number, registry: Hex, a: UpdateKeyAuthorization) {\n return {\n domain: registryDomain(chainId, registry),\n types: { UpdateKey: AUTHORIZATION_TYPES.UpdateKey },\n primaryType: 'UpdateKey' as const,\n message: { owner: a.owner, index: a.index, pgpPublicKey: stringToHex(a.pgpPublicKey), nonce: a.nonce, deadline: a.deadline },\n }\n}\n\nexport function revokeTypedData(chainId: number, registry: Hex, a: RevokeAuthorization) {\n return {\n domain: registryDomain(chainId, registry),\n types: { Revoke: AUTHORIZATION_TYPES.Revoke },\n primaryType: 'Revoke' as const,\n message: { owner: a.owner, index: a.index, nonce: a.nonce, deadline: a.deadline },\n }\n}\n\nexport function setRecordTypedData(chainId: number, registry: Hex, a: SetRecordAuthorization) {\n return {\n domain: registryDomain(chainId, registry),\n types: { SetRecord: AUTHORIZATION_TYPES.SetRecord },\n primaryType: 'SetRecord' as const,\n message: { owner: a.owner, index: a.index, kind: a.kind, value: a.value, nonce: a.nonce, deadline: a.deadline },\n }\n}\n\n/** The digest the registry recovers the signer from — handy for tests and CLIs. */\nexport function authorizationDigest(typedData: ReturnType<typeof attestTypedData | typeof reattestTypedData | typeof updateKeyTypedData | typeof revokeTypedData | typeof setRecordTypedData>): Hex {\n return hashTypedData(typedData as any)\n}\n\n/** `bytes32 kind` for a record from a human-readable name, e.g. recordKind('thurin.vouch'). */\nexport function recordKind(name: string): Hex {\n return keccak256(stringToHex(name))\n}\n"]}
@@ -1427,25 +1427,6 @@ async function hasEmailUserID(armoredKey) {
1427
1427
  return false;
1428
1428
  }
1429
1429
  }
1430
- var KEYSERVER_BASE = "https://keys.openpgp.org/vks/v1";
1431
- async function fetchKeyByFingerprint(fingerprint) {
1432
- try {
1433
- const resp = await fetch(`${KEYSERVER_BASE}/by-fingerprint/${fingerprint}`);
1434
- if (!resp.ok) return null;
1435
- return await resp.text();
1436
- } catch {
1437
- return null;
1438
- }
1439
- }
1440
- async function fetchKeyByKeyId(keyId) {
1441
- try {
1442
- const resp = await fetch(`${KEYSERVER_BASE}/by-keyid/${keyId}`);
1443
- if (!resp.ok) return null;
1444
- return await resp.text();
1445
- } catch {
1446
- return null;
1447
- }
1448
- }
1449
1430
 
1450
1431
  // src/core/efp.ts
1451
1432
  var EFP_API = "https://api.ethfollow.xyz/api/v1";
@@ -1716,6 +1697,42 @@ async function verifyProof(proof, fingerprint, neynarApiKey) {
1716
1697
  return fn(proof, fingerprint, neynarApiKey);
1717
1698
  }
1718
1699
 
1700
+ // src/core/ensHint.ts
1701
+ import { namehash, normalize } from "viem/ens";
1702
+ var ENS_HINT_KEY = "id.thurin";
1703
+ function ensHintFor(record, verifiedFingerprint) {
1704
+ const raw = record?.trim() ?? "";
1705
+ const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null;
1706
+ if (!raw) return { key: ENS_HINT_KEY, state: "unset", record: null, fingerprint: null, expected };
1707
+ const fingerprint = normalizeFingerprint(raw);
1708
+ if (!fingerprint) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint: null, expected, reason: "The record is not a PGP fingerprint" };
1709
+ if (!expected) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The address has no verified claim" };
1710
+ if (fingerprint === expected) return { key: ENS_HINT_KEY, state: "match", record: raw, fingerprint, expected };
1711
+ return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The record names a key this address has not claimed" };
1712
+ }
1713
+ function ensHintValue(fingerprint) {
1714
+ const fp = normalizeFingerprint(fingerprint);
1715
+ if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`);
1716
+ return fp.toUpperCase();
1717
+ }
1718
+ var ENS_TEXT_RESOLVER_ABI = [
1719
+ { type: "function", name: "text", stateMutability: "view", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }], outputs: [{ name: "", type: "string" }] },
1720
+ { type: "function", name: "setText", stateMutability: "nonpayable", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }, { name: "value", type: "string" }], outputs: [] }
1721
+ ];
1722
+ function ensHintWrite(name, fingerprint) {
1723
+ const normalized = normalize(name);
1724
+ return {
1725
+ abi: ENS_TEXT_RESOLVER_ABI,
1726
+ functionName: "setText",
1727
+ args: [namehash(normalized), ENS_HINT_KEY, ensHintValue(fingerprint)],
1728
+ name: normalized
1729
+ };
1730
+ }
1731
+ async function fetchEnsHint(client, name, verifiedFingerprint) {
1732
+ const record = await client.getEnsText({ name: normalize(name), key: ENS_HINT_KEY });
1733
+ return ensHintFor(record, verifiedFingerprint);
1734
+ }
1735
+
1719
1736
  // src/core/authorization.ts
1720
1737
  import { hashTypedData, keccak256 as keccak2562, stringToHex } from "viem";
1721
1738
  var EIP712_NAME = "Thurin PGPRegistry";
@@ -1835,14 +1852,18 @@ export {
1835
1852
  verifyAttestation,
1836
1853
  stripEmailUserIDs,
1837
1854
  hasEmailUserID,
1838
- fetchKeyByFingerprint,
1839
- fetchKeyByKeyId,
1840
1855
  fetchEFPGraph,
1841
1856
  identifyProof,
1842
1857
  displayUrl,
1843
1858
  proofHref,
1844
1859
  proofSecondaryHref,
1845
1860
  verifyProof,
1861
+ ENS_HINT_KEY,
1862
+ ensHintFor,
1863
+ ensHintValue,
1864
+ ENS_TEXT_RESOLVER_ABI,
1865
+ ensHintWrite,
1866
+ fetchEnsHint,
1846
1867
  EIP712_NAME,
1847
1868
  EIP712_VERSION,
1848
1869
  registryDomain,
@@ -1855,4 +1876,4 @@ export {
1855
1876
  authorizationDigest,
1856
1877
  recordKind
1857
1878
  };
1858
- //# sourceMappingURL=chunk-GKL42VHZ.js.map
1879
+ //# sourceMappingURL=chunk-FN5VSB3U.js.map