signet-verify 0.7.0 → 0.7.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
@@ -2,6 +2,12 @@
2
2
 
3
3
  Add privacy-preserving age verification to any website. One script tag, one function call. No personal data collected.
4
4
 
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install signet-verify
9
+ ```
10
+
5
11
  ## Quick Start
6
12
 
7
13
  ```html
@@ -111,7 +117,7 @@ again. This was a real, 100%-reproducible failure before 0.5.2.
111
117
  | `denied` | The user rejected the request | Nothing; respect it |
112
118
  | `aborted` | You aborted via `abortSignal` | Nothing |
113
119
  | `relay-error` | The socket failed | A retry, anchored to the same `issuedAt` |
114
- | `relay-refused` | The relay closed the subscription — `err.reason` has its text (e.g. `auth-required`) | A relay that serves gift wraps to an unauthenticated reader. `relay.damus.io` currently does not; `relay.trotters.cc` (the Signet app's relay) does |
120
+ | `relay-refused` | The relay closed the subscription — `err.reason` has its text (e.g. `auth-required`) | A relay that serves gift wraps to an unauthenticated reader. `relay.damus.io` currently does not; `nos.lol` (the default) does |
115
121
  | `invalid-issued-at` | `issuedAt` looks like the wrong unit (e.g. milliseconds) or is too far in the future | A bug in your code, not a retry — check you're passing unix **seconds** |
116
122
 
117
123
  The code is on both `err.message` and `err.code` — branch on either.
@@ -129,7 +135,7 @@ Returns `Promise<SignetVerifyResult>`
129
135
 
130
136
  | Option | Type | Default | Description |
131
137
  |--------|------|---------|-------------|
132
- | `relayUrl` | `string` | `DEFAULT_RELAY_URL` (`wss://relay.trotters.cc`) | Relay URL for cross-device communication. Must serve kind-1059 gift wraps to an unauthenticated reader |
138
+ | `relayUrl` | `string` | `DEFAULT_RELAY_URL` (`wss://nos.lol`) | Relay URL for cross-device communication. Must serve kind-1059 gift wraps to an unauthenticated reader |
133
139
  | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Modal colour scheme |
134
140
  | `timeout` | `number` | `120000` | Timeout in milliseconds |
135
141
  | `verifierCheckUrl` | `string \| null` | `'https://verify.signet.forgesworn.dev'` | Verification bot URL. Set to `null` to skip. |
@@ -29,7 +29,7 @@ export interface SignetVerifyResult {
29
29
  export interface SignetVerifyOptions {
30
30
  /** Required age range to verify (e.g., '18+', '13-17') */
31
31
  requiredAgeRange: string;
32
- /** Relay URL for cross-device communication. Default: `DEFAULT_RELAY_URL` (wss://relay.trotters.cc). */
32
+ /** Relay URL for cross-device communication. Default: `DEFAULT_RELAY_URL` (wss://nos.lol). */
33
33
  relayUrl?: string;
34
34
  /** Callback URL for same-device flow */
35
35
  callbackUrl?: string;
@@ -72,7 +72,7 @@ export declare const AUTH_FRESHNESS_WINDOW_SEC = 300;
72
72
  * relay.damus.io, the previous default, refuses those reads and has AUTH
73
73
  * misconfigured, so nothing can fetch a response from it.
74
74
  */
75
- export declare const DEFAULT_RELAY_URL = "wss://relay.trotters.cc";
75
+ export declare const DEFAULT_RELAY_URL = "wss://nos.lol";
76
76
  /** Options for waiting on a cross-device Sign-in-with-Signet response. */
77
77
  export interface WaitForAuthOptions {
78
78
  /** The challenge (also the `requestId`) sent in the auth URL. 64-char hex. */
@@ -4,7 +4,7 @@
4
4
  <div id="signet-qr" style="display:flex;justify-content:center;margin-bottom:24px;"></div>
5
5
  <p style="margin:0 0 16px;color:${l};font-size:0.8rem;">Waiting for verification...</p>
6
6
  <button id="signet-cancel" style="background:none;border:1px solid ${l};color:${y};padding:10px 24px;border-radius:8px;cursor:pointer;font-size:0.9rem;">Cancel</button>
7
- `,document.body.appendChild(x),x.showModal();let A=x.querySelector("#signet-qr");if(A){let L=document.createElement("div");L.style.cssText=`width:200px;height:200px;background:${u?"#2a2a3e":"#f0f0f0"};border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:0.75rem;color:${l};word-break:break-all;padding:12px;`,L.textContent=`signet:verify:${c.slice(0,40)}...`,A.appendChild(L)}let B=new BroadcastChannel("signet-verify-"+o);x.querySelector("#signet-cancel")?.addEventListener("click",()=>{B.close(),x.close(),x.remove(),a.remove(),f({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"cancelled"})}),B.onmessage=async L=>{let D=L.data;if(typeof D!="object"||D===null)return;let P=D;if(P.type!=="signet-verify-response"||P.requestId!==o||!P.credential||typeof P.credential!="object"||!Array.isArray(P.credential.tags))return;let j=P.credential,G=await Ki(j),K=Gt(j.tags,"age-range"),z=Gt(j.tags,"tier"),Y=Gt(j.tags,"entity-type"),$=Gt(j.tags,"expires"),F=K?zi(K,n.requiredAgeRange):!1,M=await Yi(j.pubkey,n.verifierCheckUrl),C=M?.confirmed??null,H=M?.method??null,h=n.acceptUnconfirmed||C===!0;x.close(),x.remove(),a.remove(),B.close();let b=z?parseInt(z,10):null,v=$?parseInt($,10):null,S=Math.floor(Date.now()/1e3),O=v===null||!isNaN(v)&&v>S,p;G?O?F?h||(p=C===!1?"verifier-not-confirmed":"verifier-check-unavailable"):p="age-range-not-met":p="credential-expired":p="invalid-credential",f({verified:G&&O&&F&&h,ageRange:K||null,tier:b!==null&&!isNaN(b)?b:null,entityType:Y||null,credentialId:j.id,verifierPubkey:j.pubkey,verifierConfirmed:C,verifierMethod:H,issuedAt:j.created_at,expiresAt:v!==null&&!isNaN(v)?v:null,error:p})},setTimeout(()=>{x.close(),x.remove(),a.remove(),B.close(),f({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"timeout"})},n.timeout)})}var ot=300,Qo="wss://relay.trotters.cc",Xo=60;function Qn(t){let e=JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content]),r=$e(new TextEncoder().encode(e));return Re(r)}async function Wi(t,e){try{let r=Xn(e,t.pubkey),n=Jn(t.content,r),o=JSON.parse(n);if(typeof o!="object"||o===null)return null;let s=o;if(s.kind!==13||typeof s.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(s.pubkey)||typeof s.created_at!="number"||!Array.isArray(s.tags)||typeof s.content!="string"||typeof s.id!="string"||!/^[0-9a-f]{64}$/i.test(s.id)||typeof s.sig!="string"||!/^[0-9a-f]{128}$/i.test(s.sig)||Qn({pubkey:s.pubkey,created_at:s.created_at,kind:13,tags:s.tags,content:s.content})!==s.id.toLowerCase())return null;let c=Ae(s.sig),f=Ae(s.id),a=Ae(s.pubkey);if(!at.verify(c,f,a))return null;let u=Xn(e,s.pubkey),d=Jn(s.content,u),y=JSON.parse(d);if(typeof y!="object"||y===null)return null;let l=y;if(typeof l.pubkey!="string"||l.pubkey!==s.pubkey||typeof l.kind!="number"||typeof l.created_at!="number"||!Array.isArray(l.tags)||typeof l.content!="string")return null;let x=typeof l.id=="string"?l.id:Qn({pubkey:l.pubkey,created_at:l.created_at,kind:l.kind,tags:l.tags,content:l.content});return{pubkey:l.pubkey,id:x,kind:l.kind,created_at:l.created_at,tags:l.tags,content:l.content}}catch{return null}}function Gi(t){return es(t,64)}function es(t,e){if(typeof t!="string")return;let r=t.replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069]/g,"").trim().slice(0,e);return r.length>0?r:void 0}async function ts(t){let e=a=>{let u=new Error(a);return u.code=a,u};if(!/^[0-9a-f]{64}$/i.test(t.requestId))throw e("invalid-request-id");if(!(t.sessionPrivKey instanceof Uint8Array)||t.sessionPrivKey.length!==32)throw e("invalid-session-privkey");if(!/^wss:\/\//i.test(t.relayUrl)&&!/^ws:\/\/(localhost|127\.0\.0\.1)([:\/]|$)/i.test(t.relayUrl))throw e("invalid-relay-url");if(typeof t.expectedOrigin!="string"||t.expectedOrigin.length===0)throw e("invalid-expected-origin");if(Number.isFinite(t.issuedAt)&&t.issuedAt>Math.floor(Date.now()/1e3)+ot)throw e("invalid-issued-at");let r=Re(at.getPublicKey(t.sessionPrivKey)),n=Number.isFinite(t.issuedAt)?(Math.floor(t.issuedAt)+ot)*1e3-Date.now():12e4,o=Math.max(5e3,Math.min(t.timeout??n,6e5)),s=t.requestId.toLowerCase(),i=t.expectedOrigin,c=Number.isFinite(t.since)?Math.floor(t.since):Number.isFinite(t.issuedAt)?Math.floor(t.issuedAt)-Xo:Math.floor(Date.now()/1e3)-Xo,f=()=>Number.isFinite(t.issuedAt)&&Date.now()>=(Math.floor(t.issuedAt)+ot)*1e3-1e3;return new Promise((a,u)=>{let d=`sa-${Math.random().toString(36).slice(2,12)}`,y=!1,l=!1,x,A,B=Date.now()+o,L=F=>{if(!y){y=!0,clearTimeout(P),clearTimeout(A),typeof document<"u"&&document.removeEventListener("visibilitychange",G),t.abortSignal?.removeEventListener("abort",$);try{x?.close()}catch{}F()}},D=()=>L(()=>u(e(l||f()?"expired":"timeout"))),P=setTimeout(()=>{D()},o),j=()=>{!y&&x?.readyState===1&&x.send(JSON.stringify(["REQ",d,{kinds:[1059],"#p":[r],since:c}]))},G=()=>{if(!(y||document.visibilityState!=="visible")){if(Date.now()>=B)return D();!x||x.readyState>=2?Y():j()}};typeof document<"u"&&document.addEventListener("visibilitychange",G);let K=async F=>{if(y)return;let M;try{M=JSON.parse(typeof F.data=="string"?F.data:"")}catch{return}if(!Array.isArray(M))return;if(M[0]==="CLOSED"&&M[1]===d){let U=es(M[2],200);L(()=>{let V=e("relay-refused");U&&(V.reason=U),u(V)});return}if(M[0]!=="EVENT"||M[1]!==d)return;let C=M[2];if(typeof C!="object"||C===null)return;let H=C;if(H.kind!==1059||typeof H.pubkey!="string"||typeof H.content!="string")return;let h=await Wi({pubkey:H.pubkey,content:H.content},t.sessionPrivKey);if(!h||h.kind!==29999)return;let b=h.tags.find(U=>U[0]==="session");if(!b||b[1]!==s)return;let v=h.tags.find(U=>U[0]==="status");if(v?.[1]==="rejected"){L(()=>u(e("denied")));return}if(v?.[1]!=="approved")return;if(Math.abs(Date.now()/1e3-h.created_at)>ot){l=!0;return}let O;try{let U=JSON.parse(h.content);if(typeof U!="object"||U===null)return;O=U}catch{return}if(O.type!=="signet-auth-response"||O.requestId!==s||typeof O.authEvent!="object"||O.authEvent===null)return;let p=O.authEvent;if(typeof p.id!="string"||!/^[0-9a-f]{64}$/i.test(p.id)||typeof p.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(p.pubkey)||typeof p.sig!="string"||!/^[0-9a-f]{128}$/i.test(p.sig)||p.kind!==21236||typeof p.created_at!="number"||!Array.isArray(p.tags)||typeof p.content!="string"||p.pubkey.toLowerCase()!==h.pubkey.toLowerCase()||Qn({pubkey:p.pubkey,created_at:p.created_at,kind:21236,tags:p.tags,content:p.content})!==p.id.toLowerCase())return;let w=!1;try{let U=Ae(p.sig),V=Ae(p.id),Z=Ae(p.pubkey);w=at.verify(U,V,Z)}catch{w=!1}if(!w)return;let g=p.tags,E=g.find(U=>Array.isArray(U)&&U[0]==="challenge");if(!E||typeof E[1]!="string"||E[1].toLowerCase()!==s)return;let I=g.find(U=>Array.isArray(U)&&U[0]==="origin");if(!I||I[1]!==i)return;if(Math.abs(Date.now()/1e3-p.created_at)>ot){l=!0;return}let T={id:p.id.toLowerCase(),pubkey:p.pubkey.toLowerCase(),kind:21236,created_at:p.created_at,tags:g,content:p.content,sig:p.sig},k=Gi(O.displayName),q=typeof O.bunkerUri=="string"&&/^bunker:\/\//i.test(O.bunkerUri)?O.bunkerUri:void 0;L(()=>a({pubkey:T.pubkey,authEvent:T,credential:O.credential,...k!==void 0?{displayName:k}:{},...q!==void 0?{bunkerUri:q}:{},createdAt:T.created_at}))},z=F=>{if(!y){if(typeof document>"u")return L(()=>u(e(F)));clearTimeout(A),document.visibilityState==="visible"&&(A=setTimeout(Y,1e3))}};function Y(){if(y)return;if(clearTimeout(A),Date.now()>=B)return D();let F=x;x=void 0;try{F?.close()}catch{}try{let M=new WebSocket(t.relayUrl);x=M,M.onopen=j,M.onmessage=K,M.onerror=()=>{x===M&&z("relay-error")},M.onclose=()=>{x===M&&z("relay-closed")}}catch{z("relay-error")}}let $=()=>L(()=>u(e("aborted")));if(t.abortSignal?.aborted){L(()=>u(e("aborted")));return}t.abortSignal?.addEventListener("abort",$,{once:!0}),Y()})}typeof window<"u"&&(window.Signet={verifyAge:Jo,waitForAuthResponse:ts});return fs(Xi);})();
7
+ `,document.body.appendChild(x),x.showModal();let A=x.querySelector("#signet-qr");if(A){let L=document.createElement("div");L.style.cssText=`width:200px;height:200px;background:${u?"#2a2a3e":"#f0f0f0"};border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:0.75rem;color:${l};word-break:break-all;padding:12px;`,L.textContent=`signet:verify:${c.slice(0,40)}...`,A.appendChild(L)}let B=new BroadcastChannel("signet-verify-"+o);x.querySelector("#signet-cancel")?.addEventListener("click",()=>{B.close(),x.close(),x.remove(),a.remove(),f({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"cancelled"})}),B.onmessage=async L=>{let D=L.data;if(typeof D!="object"||D===null)return;let P=D;if(P.type!=="signet-verify-response"||P.requestId!==o||!P.credential||typeof P.credential!="object"||!Array.isArray(P.credential.tags))return;let j=P.credential,G=await Ki(j),K=Gt(j.tags,"age-range"),z=Gt(j.tags,"tier"),Y=Gt(j.tags,"entity-type"),$=Gt(j.tags,"expires"),F=K?zi(K,n.requiredAgeRange):!1,M=await Yi(j.pubkey,n.verifierCheckUrl),C=M?.confirmed??null,H=M?.method??null,h=n.acceptUnconfirmed||C===!0;x.close(),x.remove(),a.remove(),B.close();let b=z?parseInt(z,10):null,v=$?parseInt($,10):null,S=Math.floor(Date.now()/1e3),O=v===null||!isNaN(v)&&v>S,p;G?O?F?h||(p=C===!1?"verifier-not-confirmed":"verifier-check-unavailable"):p="age-range-not-met":p="credential-expired":p="invalid-credential",f({verified:G&&O&&F&&h,ageRange:K||null,tier:b!==null&&!isNaN(b)?b:null,entityType:Y||null,credentialId:j.id,verifierPubkey:j.pubkey,verifierConfirmed:C,verifierMethod:H,issuedAt:j.created_at,expiresAt:v!==null&&!isNaN(v)?v:null,error:p})},setTimeout(()=>{x.close(),x.remove(),a.remove(),B.close(),f({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"timeout"})},n.timeout)})}var ot=300,Qo="wss://nos.lol",Xo=60;function Qn(t){let e=JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content]),r=$e(new TextEncoder().encode(e));return Re(r)}async function Wi(t,e){try{let r=Xn(e,t.pubkey),n=Jn(t.content,r),o=JSON.parse(n);if(typeof o!="object"||o===null)return null;let s=o;if(s.kind!==13||typeof s.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(s.pubkey)||typeof s.created_at!="number"||!Array.isArray(s.tags)||typeof s.content!="string"||typeof s.id!="string"||!/^[0-9a-f]{64}$/i.test(s.id)||typeof s.sig!="string"||!/^[0-9a-f]{128}$/i.test(s.sig)||Qn({pubkey:s.pubkey,created_at:s.created_at,kind:13,tags:s.tags,content:s.content})!==s.id.toLowerCase())return null;let c=Ae(s.sig),f=Ae(s.id),a=Ae(s.pubkey);if(!at.verify(c,f,a))return null;let u=Xn(e,s.pubkey),d=Jn(s.content,u),y=JSON.parse(d);if(typeof y!="object"||y===null)return null;let l=y;if(typeof l.pubkey!="string"||l.pubkey!==s.pubkey||typeof l.kind!="number"||typeof l.created_at!="number"||!Array.isArray(l.tags)||typeof l.content!="string")return null;let x=typeof l.id=="string"?l.id:Qn({pubkey:l.pubkey,created_at:l.created_at,kind:l.kind,tags:l.tags,content:l.content});return{pubkey:l.pubkey,id:x,kind:l.kind,created_at:l.created_at,tags:l.tags,content:l.content}}catch{return null}}function Gi(t){return es(t,64)}function es(t,e){if(typeof t!="string")return;let r=t.replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069]/g,"").trim().slice(0,e);return r.length>0?r:void 0}async function ts(t){let e=a=>{let u=new Error(a);return u.code=a,u};if(!/^[0-9a-f]{64}$/i.test(t.requestId))throw e("invalid-request-id");if(!(t.sessionPrivKey instanceof Uint8Array)||t.sessionPrivKey.length!==32)throw e("invalid-session-privkey");if(!/^wss:\/\//i.test(t.relayUrl)&&!/^ws:\/\/(localhost|127\.0\.0\.1)([:\/]|$)/i.test(t.relayUrl))throw e("invalid-relay-url");if(typeof t.expectedOrigin!="string"||t.expectedOrigin.length===0)throw e("invalid-expected-origin");if(Number.isFinite(t.issuedAt)&&t.issuedAt>Math.floor(Date.now()/1e3)+ot)throw e("invalid-issued-at");let r=Re(at.getPublicKey(t.sessionPrivKey)),n=Number.isFinite(t.issuedAt)?(Math.floor(t.issuedAt)+ot)*1e3-Date.now():12e4,o=Math.max(5e3,Math.min(t.timeout??n,6e5)),s=t.requestId.toLowerCase(),i=t.expectedOrigin,c=Number.isFinite(t.since)?Math.floor(t.since):Number.isFinite(t.issuedAt)?Math.floor(t.issuedAt)-Xo:Math.floor(Date.now()/1e3)-Xo,f=()=>Number.isFinite(t.issuedAt)&&Date.now()>=(Math.floor(t.issuedAt)+ot)*1e3-1e3;return new Promise((a,u)=>{let d=`sa-${Math.random().toString(36).slice(2,12)}`,y=!1,l=!1,x,A,B=Date.now()+o,L=F=>{if(!y){y=!0,clearTimeout(P),clearTimeout(A),typeof document<"u"&&document.removeEventListener("visibilitychange",G),t.abortSignal?.removeEventListener("abort",$);try{x?.close()}catch{}F()}},D=()=>L(()=>u(e(l||f()?"expired":"timeout"))),P=setTimeout(()=>{D()},o),j=()=>{!y&&x?.readyState===1&&x.send(JSON.stringify(["REQ",d,{kinds:[1059],"#p":[r],since:c}]))},G=()=>{if(!(y||document.visibilityState!=="visible")){if(Date.now()>=B)return D();!x||x.readyState>=2?Y():j()}};typeof document<"u"&&document.addEventListener("visibilitychange",G);let K=async F=>{if(y)return;let M;try{M=JSON.parse(typeof F.data=="string"?F.data:"")}catch{return}if(!Array.isArray(M))return;if(M[0]==="CLOSED"&&M[1]===d){let U=es(M[2],200);L(()=>{let V=e("relay-refused");U&&(V.reason=U),u(V)});return}if(M[0]!=="EVENT"||M[1]!==d)return;let C=M[2];if(typeof C!="object"||C===null)return;let H=C;if(H.kind!==1059||typeof H.pubkey!="string"||typeof H.content!="string")return;let h=await Wi({pubkey:H.pubkey,content:H.content},t.sessionPrivKey);if(!h||h.kind!==29999)return;let b=h.tags.find(U=>U[0]==="session");if(!b||b[1]!==s)return;let v=h.tags.find(U=>U[0]==="status");if(v?.[1]==="rejected"){L(()=>u(e("denied")));return}if(v?.[1]!=="approved")return;if(Math.abs(Date.now()/1e3-h.created_at)>ot){l=!0;return}let O;try{let U=JSON.parse(h.content);if(typeof U!="object"||U===null)return;O=U}catch{return}if(O.type!=="signet-auth-response"||O.requestId!==s||typeof O.authEvent!="object"||O.authEvent===null)return;let p=O.authEvent;if(typeof p.id!="string"||!/^[0-9a-f]{64}$/i.test(p.id)||typeof p.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(p.pubkey)||typeof p.sig!="string"||!/^[0-9a-f]{128}$/i.test(p.sig)||p.kind!==21236||typeof p.created_at!="number"||!Array.isArray(p.tags)||typeof p.content!="string"||p.pubkey.toLowerCase()!==h.pubkey.toLowerCase()||Qn({pubkey:p.pubkey,created_at:p.created_at,kind:21236,tags:p.tags,content:p.content})!==p.id.toLowerCase())return;let w=!1;try{let U=Ae(p.sig),V=Ae(p.id),Z=Ae(p.pubkey);w=at.verify(U,V,Z)}catch{w=!1}if(!w)return;let g=p.tags,E=g.find(U=>Array.isArray(U)&&U[0]==="challenge");if(!E||typeof E[1]!="string"||E[1].toLowerCase()!==s)return;let I=g.find(U=>Array.isArray(U)&&U[0]==="origin");if(!I||I[1]!==i)return;if(Math.abs(Date.now()/1e3-p.created_at)>ot){l=!0;return}let T={id:p.id.toLowerCase(),pubkey:p.pubkey.toLowerCase(),kind:21236,created_at:p.created_at,tags:g,content:p.content,sig:p.sig},k=Gi(O.displayName),q=typeof O.bunkerUri=="string"&&/^bunker:\/\//i.test(O.bunkerUri)?O.bunkerUri:void 0;L(()=>a({pubkey:T.pubkey,authEvent:T,credential:O.credential,...k!==void 0?{displayName:k}:{},...q!==void 0?{bunkerUri:q}:{},createdAt:T.created_at}))},z=F=>{if(!y){if(typeof document>"u")return L(()=>u(e(F)));clearTimeout(A),document.visibilityState==="visible"&&(A=setTimeout(Y,1e3))}};function Y(){if(y)return;if(clearTimeout(A),Date.now()>=B)return D();let F=x;x=void 0;try{F?.close()}catch{}try{let M=new WebSocket(t.relayUrl);x=M,M.onopen=j,M.onmessage=K,M.onerror=()=>{x===M&&z("relay-error")},M.onclose=()=>{x===M&&z("relay-closed")}}catch{z("relay-error")}}let $=()=>L(()=>u(e("aborted")));if(t.abortSignal?.aborted){L(()=>u(e("aborted")));return}t.abortSignal?.addEventListener("abort",$,{once:!0}),Y()})}typeof window<"u"&&(window.Signet={verifyAge:Jo,waitForAuthResponse:ts});return fs(Xi);})();
8
8
  /*! Bundled license information:
9
9
 
10
10
  @noble/curves/utils.js:
@@ -299,7 +299,7 @@ export const AUTH_FRESHNESS_WINDOW_SEC = 300;
299
299
  * relay.damus.io, the previous default, refuses those reads and has AUTH
300
300
  * misconfigured, so nothing can fetch a response from it.
301
301
  */
302
- export const DEFAULT_RELAY_URL = 'wss://relay.trotters.cc';
302
+ export const DEFAULT_RELAY_URL = 'wss://nos.lol';
303
303
  /**
304
304
  * Slack subtracted from `issuedAt` when deriving the relay query window, to
305
305
  * absorb clock skew between the consumer and the signer device. Widening the
package/llms.txt ADDED
@@ -0,0 +1,67 @@
1
+ # Signet Verify
2
+
3
+ > Age verification on a website usually means collecting ID or sending users
4
+ > off to a third-party checker. signet-verify is a browser SDK that checks a
5
+ > cryptographic age credential from the Signet app instead, without the site
6
+ > ever seeing personal data.
7
+
8
+ For websites that need to gate content by age range (adult content, age
9
+ brackets for minors) without running their own verification flow. A single
10
+ function call opens a QR/modal, waits for the user's Signet app to respond,
11
+ and checks both the credential and the issuing verifier's professional
12
+ registration.
13
+
14
+ ## Getting Started
15
+
16
+ ```bash
17
+ npm install signet-verify
18
+ ```
19
+
20
+ ```javascript
21
+ import { verifyAge } from 'signet-verify';
22
+
23
+ const result = await verifyAge('18+');
24
+ if (result.verified) {
25
+ // user confirmed 18+ by a confirmed professional verifier
26
+ }
27
+ ```
28
+
29
+ For a script tag / CDN build, use the IIFE export (`signet-verify/iife`),
30
+ which exposes `window.Signet`.
31
+
32
+ ## Key Concepts
33
+
34
+ - **Credential, not identity**: the proof carries an age range and verifier
35
+ info, never a name, date of birth or other PII.
36
+ - **Verifier confirmation**: by default a result only counts as `verified`
37
+ when the issuing verifier is checked against a public professional
38
+ register (GMC, SRA, TRA, etc.) via a verification bot; this can be relaxed
39
+ with `acceptUnconfirmed` or pointed at a different bot with
40
+ `verifierCheckUrl`.
41
+ - **Same-device vs cross-device**: `verifyAge` covers the common same-device
42
+ QR/modal flow; `waitForAuthResponse` is the lower-level cross-device
43
+ primitive for a consumer that mints its own challenge and waits on a relay
44
+ for the signed response.
45
+ - **The sign-in deadline**: a cross-device wait has one deadline,
46
+ `issuedAt + AUTH_FRESHNESS_WINDOW_SEC`. `issuedAt` must be passed on every
47
+ call for the same sign-in, including retries after the page was
48
+ backgrounded, or the relay query anchor is wrong.
49
+
50
+ ## API Surface
51
+
52
+ - `verifyAge(requiredAgeRange, options?) => Promise<SignetVerifyResult>`:
53
+ the same-device verification flow.
54
+ - `waitForAuthResponse(options) => Promise<SignetAuthResult>`: the
55
+ cross-device primitive; see the README for the full option set
56
+ (`requestId`, `relayUrl`, `sessionPrivKey`, `expectedOrigin`, `issuedAt`,
57
+ `abortSignal`).
58
+ - `AUTH_FRESHNESS_WINDOW_SEC`, `DEFAULT_RELAY_URL`: constants used to
59
+ compute and check the sign-in deadline.
60
+ - Result and option types: `SignetVerifyResult`, `SignetVerifyOptions`,
61
+ `WaitForAuthOptions`, `SignetAuthResult`, `SignetAuthEvent`.
62
+
63
+ ## Optional
64
+
65
+ - Full API reference, error codes and age range values: [README](README.md)
66
+ - Cross-device example: [examples/cross-device-login.html](examples/cross-device-login.html)
67
+ - Release history: [CHANGELOG.md](CHANGELOG.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signet-verify",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Signet SDK — website age verification and cross-device Sign-in-with-Signet",
5
5
  "type": "module",
6
6
  "main": "./dist/signet-verify.js",
@@ -14,7 +14,25 @@
14
14
  "./iife": "./dist/signet-verify.iife.js"
15
15
  },
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "llms.txt"
19
+ ],
20
+ "homepage": "https://github.com/forgesworn/signet-verify#readme",
21
+ "keywords": [
22
+ "signet",
23
+ "age-verification",
24
+ "age-gate",
25
+ "nostr",
26
+ "credential",
27
+ "verifiable-credential",
28
+ "cross-device",
29
+ "sign-in",
30
+ "authentication",
31
+ "privacy",
32
+ "gdpr",
33
+ "compliance",
34
+ "sdk",
35
+ "browser"
18
36
  ],
19
37
  "publishConfig": {
20
38
  "provenance": true