@voidly/session 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var __typeError=msg=>{throw TypeError(msg)};var __accessCheck=(obj,member,msg)=>member.has(obj)||__typeError("Cannot "+msg);var __privateGet=(obj,member,getter)=>(__accessCheck(obj,member,"read from private field"),getter?getter.call(obj):member.get(obj)),__privateAdd=(obj,member,value)=>member.has(obj)?__typeError("Cannot add the same private member more than once"):member instanceof WeakSet?member.add(obj):member.set(obj,value);import __naclUtil0 from"tweetnacl-util";const{decodeBase64: decodeBase649}=__naclUtil0;import nacl from"tweetnacl";import __naclUtil1 from"tweetnacl-util";const{decodeBase64}=__naclUtil1;var MIN_NONCE_LENGTH=16,MAX_WINDOW_MS=3600*1e3,MAX_CLOCK_SKEW_MS=30*1e3;function canonicalize(value){if(value==null)return"null";if(typeof value=="boolean")return value?"true":"false";if(typeof value=="number"){if(!Number.isFinite(value)||!Number.isInteger(value))throw new Error("canonicalize: only finite integers supported");return value.toString(10)}if(typeof value=="bigint")return value.toString(10);if(typeof value=="string")return JSON.stringify(value);if(Array.isArray(value))return"["+value.map(canonicalize).join(",")+"]";if(typeof value=="object"){let obj=value;return"{"+Object.keys(obj).filter(k=>obj[k]!==null&&obj[k]!==void 0).sort().map(k=>JSON.stringify(k)+":"+canonicalize(obj[k])).join(",")+"}"}throw new Error(`canonicalize: unsupported type ${typeof value}`)}function canonicalBytes(value){return new TextEncoder().encode(canonicalize(value))}async function envelopeHash(env){let bytes=canonicalBytes(env),buf=await crypto.subtle.digest("SHA-256",bytes);return Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,"0")).join("")}function verifyEnvelopeSignature(env,signatureBase64,publicKey){try{if(publicKey.length!==32)return!1;let sig=decodeBase64(signatureBase64);if(sig.length!==64)return!1;let bytes=canonicalBytes(env);return nacl.sign.detached.verify(bytes,sig,publicKey)}catch{return!1}}var BASE58_ALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";function toBase58(bytes){let result2="",num=BigInt("0x"+Array.from(bytes).map(b=>b.toString(16).padStart(2,"0")).join(""));for(;num>0n;){let remainder=num%58n;num=num/58n,result2=BASE58_ALPHABET[Number(remainder)]+result2}for(let byte of bytes)if(byte===0)result2="1"+result2;else break;return result2||"1"}function deriveDidFromSigningKey(signingPublicKey){return`did:voidly:${toBase58(signingPublicKey.slice(0,16))}`}import __naclUtil2 from"tweetnacl-util";const{encodeBase64}=__naclUtil2;function verifyDetached(env,signatureBase64,publicKey){return verifyEnvelopeSignature(env,signatureBase64,publicKey)}async function signCanonical(env,sign){try{let sig=await sign(canonicalBytes(env));return!(sig instanceof Uint8Array)||sig.length!==64?null:encodeBase64(sig)}catch{return null}}var CAPSULE_NONCE_LENGTH=24;async function sha256Bytes(bytes){let buf=await crypto.subtle.digest("SHA-256",bytes);return new Uint8Array(buf)}async function sha256Hex(bytes){let digest=await sha256Bytes(bytes);return Array.from(digest).map(b=>b.toString(16).padStart(2,"0")).join("")}function isAllZero(bytes){for(let i=0;i<bytes.length;i++)if(bytes[i]!==0)return!1;return!0}var FRAME_OFFER_HASH_BYTES=32,FRAME_BODY_NONCE_BYTES=24,FRAME_HEADER_LENGTH=FRAME_OFFER_HASH_BYTES+FRAME_BODY_NONCE_BYTES+4,MIN_FRAME_BUCKET=512;function frameBucketSize(totalBytes){let size=MIN_FRAME_BUCKET;for(;size<totalBytes;)size*=2;return size}var SECRETBOX_TAG_BYTES=16,CAPSULE_SALT_BYTES=32;function base64Length(bytes){return 4*Math.ceil(bytes/3)}var SESSION_MAX_BODY_BYTES=32*1024,SESSION_WORST_CASE_ENVELOPE_BYTES=4972,MAX_FRAME_BUCKET_BYTES=(()=>{let best=0;for(let bucket=MIN_FRAME_BUCKET;bucket<=SESSION_MAX_BODY_BYTES;bucket*=2)base64Length(bucket+SECRETBOX_TAG_BYTES)+SESSION_WORST_CASE_ENVELOPE_BYTES<=SESSION_MAX_BODY_BYTES&&(best=bucket);if(best===0)throw new Error("wireBudget: the transport ceiling cannot carry the minimum frame bucket");return best})(),MAX_CAPSULE_BODY_BYTES=MAX_FRAME_BUCKET_BYTES+SECRETBOX_TAG_BYTES,MAX_CAPSULE_BODY_BASE64_LENGTH=base64Length(MAX_CAPSULE_BODY_BYTES),MAX_SEALED_PAYLOAD_BYTES=MAX_FRAME_BUCKET_BYTES-FRAME_HEADER_LENGTH,SESSION_EVIDENCE_HEADROOM_BYTES=SESSION_MAX_BODY_BYTES-MAX_CAPSULE_BODY_BASE64_LENGTH-SESSION_WORST_CASE_ENVELOPE_BYTES;var SESSION_OFFER_SCHEMA="voidly-session-offer/v1",TASK_CAPSULE_SCHEMA="voidly-task-capsule/v1",TASK_GRANT_SCHEMA="voidly-task-grant/v1",TASK_ACCEPTANCE_SCHEMA="voidly-task-acceptance/v1",TASK_BRIEF_SCHEMA="voidly-task-brief/v1",TASK_RESULT_CAPSULE_SCHEMA="voidly-task-result-capsule/v1",TASK_RESULT_SCHEMA="voidly-task-result/v1",TASK_DELIVERY_SCHEMA="voidly-task-delivery/v1",TASK_RECOVERY_SCHEMA="voidly-task-recovery/v1",CAPSULE_ALG="x25519-xsalsa20-poly1305+xsalsa20-poly1305",RESULT_CAPSULE_ALG="xsalsa20-poly1305",MAX_OFFER_TTL_MS=1440*60*1e3,MAX_GRANT_TTL_MS=1440*60*1e3,MAX_ACCEPTANCE_TTL_MS=MAX_WINDOW_MS,SESSION_RAIL_MIN_CONFIRMATIONS=12,SESSION_RAIL_BLOCK_TIME_MS=2e3,MIN_GRANT_TTL_MS=SESSION_RAIL_MIN_CONFIRMATIONS*SESSION_RAIL_BLOCK_TIME_MS+MAX_CLOCK_SKEW_MS,MAX_SERVICE_REF_LENGTH=128,MAX_NONCE_LENGTH=128,BRIEF_SALT_BASE64_LENGTH=base64Length(CAPSULE_SALT_BYTES),canonicalPayloadOverhead=(schema,field)=>canonicalBytes({schema,[field]:"",salt_base64:"A".repeat(BRIEF_SALT_BASE64_LENGTH)}).length,MAX_BRIEF_LENGTH=MAX_SEALED_PAYLOAD_BYTES-Math.max(canonicalPayloadOverhead(TASK_BRIEF_SCHEMA,"brief"),canonicalPayloadOverhead(TASK_RESULT_SCHEMA,"result"));var MAX_RESULT_LENGTH=MAX_BRIEF_LENGTH;var MAX_RESULT_BODY_BASE64_LENGTH=MAX_CAPSULE_BODY_BASE64_LENGTH,MAX_RECOVERY_TTL_MS=10080*60*1e3,MIN_PLAUSIBLE_NOW_MS=16e11,MAX_PLAUSIBLE_NOW_MS=41024448e5,DID_RE=/^did:voidly:[A-Za-z0-9._-]{1,64}$/,HEX64_RE=/^[0-9a-f]{64}$/,OFFER_KEYS=["schema","hirer_did","hirer_signing_pubkey_base64","provider_did","service_ref","price_chain","price_asset","price_payer_account","price_payee_account","price_min_amount","price_max_amount","nonce","issued_at","expires_at"],CAPSULE_KEYS=["schema","alg","offer_hash","recipient_enc_pubkey_base64","ephemeral_pubkey_base64","wrapped_session_key_base64","wrap_nonce_base64","body_nonce_base64","body_base64"],GRANT_KEYS=["schema","hirer_did","provider_did","provider_signing_pubkey_base64","provider_enc_pubkey_base64","offer_hash","capsule_hash","brief_commitment","price_chain","price_asset","price_payer_account","price_payee_account","price_min_amount","price_max_amount","nonce","issued_at","expires_at"],ACCEPTANCE_KEYS=["schema","grant_hash","redeemer_did","action_nonce","issued_at","expires_at"],RESULT_CAPSULE_KEYS=["schema","alg","grant_hash","body_nonce_base64","body_base64"],DELIVERY_KEYS=["schema","grant_hash","offer_hash","provider_did","result_capsule_hash","result_commitment","issued_at","recoverable_until"],RECOVERY_KEYS=["schema","grant_hash","requester_did","action_nonce","issued_at","expires_at"];function hasOnlyKeys(raw,allowed){for(let k of Object.keys(raw))if(!allowed.includes(k))return!1;return!0}function isBase64Key32(value,decode){if(typeof value!="string"||value.length===0)return!1;try{return decode(value).length===32}catch{return!1}}function isNonce(value,minLength){return typeof value=="string"&&value.length>=minLength&&value.length<=MAX_NONCE_LENGTH}var ISO_UTC_RE=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/;function timestampMs(value){if(typeof value!="string")return null;let m=ISO_UTC_RE.exec(value);if(!m)return null;let year=Number(m[1]),month=Number(m[2]),day=Number(m[3]),hour=Number(m[4]),minute=Number(m[5]),second=Number(m[6]),milli=m[7]===void 0?0:Number(m[7]);if(month<1||month>12||day<1||day>31||hour>23||minute>59||second>59)return null;let ms=Date.UTC(year,month-1,day,hour,minute,second,milli);if(!Number.isFinite(ms))return null;let d=new Date(ms);return d.getUTCFullYear()!==year||d.getUTCMonth()!==month-1||d.getUTCDate()!==day?null:ms}function isPlausibleNowMs(nowMs){return typeof nowMs=="number"&&Number.isInteger(nowMs)&&nowMs>=MIN_PLAUSIBLE_NOW_MS&&nowMs<=MAX_PLAUSIBLE_NOW_MS}function validateAcceptance(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"clock_implausible"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"acceptance_not_object"};let r={...raw};if(r.schema!==TASK_ACCEPTANCE_SCHEMA)return{ok:!1,reason:"acceptance_schema_mismatch"};if(!hasOnlyKeys(r,ACCEPTANCE_KEYS))return{ok:!1,reason:"acceptance_unexpected_field"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"acceptance_invalid_grant_hash"};if(typeof r.redeemer_did!="string"||!DID_RE.test(r.redeemer_did))return{ok:!1,reason:"acceptance_invalid_redeemer_did"};if(!isNonce(r.action_nonce,MIN_NONCE_LENGTH))return{ok:!1,reason:"acceptance_invalid_nonce"};let issuedAt=timestampMs(r.issued_at),expiresAt=timestampMs(r.expires_at);if(issuedAt===null||expiresAt===null)return{ok:!1,reason:"acceptance_invalid_timestamp"};let window=expiresAt-issuedAt;return window<=0?{ok:!1,reason:"acceptance_invalid_timestamp"}:window>MAX_ACCEPTANCE_TTL_MS?{ok:!1,reason:"acceptance_window_too_long"}:nowMs<issuedAt-MAX_CLOCK_SKEW_MS?{ok:!1,reason:"acceptance_not_yet_valid"}:nowMs>expiresAt?{ok:!1,reason:"acceptance_expired"}:{ok:!0,env:{schema:TASK_ACCEPTANCE_SCHEMA,grant_hash:r.grant_hash,redeemer_did:r.redeemer_did,action_nonce:r.action_nonce,issued_at:r.issued_at,expires_at:r.expires_at}}}async function buildAcceptance(input){let acceptance={schema:TASK_ACCEPTANCE_SCHEMA,grant_hash:input.grantHash,redeemer_did:input.redeemerDid,action_nonce:input.actionNonce,issued_at:new Date(input.nowMs).toISOString(),expires_at:new Date(input.nowMs+input.ttlMs).toISOString()},check=validateAcceptance(acceptance,input.nowMs);if(!check.ok)return{ok:!1,reason:check.reason};let signature=await signCanonical(check.env,input.sign);return signature===null?{ok:!1,reason:"signer_failed"}:{ok:!0,acceptance:check.env,signature_base64:signature}}var CAIP2_RE=/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/,CAIP10_RE=/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}:[-.%a-zA-Z0-9]{1,128}$/,CAIP19_RE=/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}\/[-a-z0-9]{3,8}:[-.%a-zA-Z0-9]{1,128}(\/[-.%a-zA-Z0-9]{1,78})?$/,POSITIVE_DECIMAL_RE=/^[1-9][0-9]{0,77}$/;function isCaip2(s){return typeof s=="string"&&CAIP2_RE.test(s)}function isCaip10(s){return typeof s=="string"&&CAIP10_RE.test(s)}function isCaip19(s){return typeof s=="string"&&CAIP19_RE.test(s)}function isPositiveDecimalString(s){return typeof s=="string"&&POSITIVE_DECIMAL_RE.test(s)}function compareDecimalStrings(a,b){return!isPositiveDecimalString(a)||!isPositiveDecimalString(b)?null:a.length!==b.length?a.length<b.length?-1:1:a<b?-1:a>b?1:0}function caip2Of(caip10OrCaip19){if(typeof caip10OrCaip19!="string")return null;if(isCaip19(caip10OrCaip19)){let chain=caip10OrCaip19.slice(0,caip10OrCaip19.indexOf("/"));return isCaip2(chain)?chain:null}if(isCaip10(caip10OrCaip19)){let parts=caip10OrCaip19.split(":"),chain=`${parts[0]}:${parts[1]}`;return isCaip2(chain)?chain:null}return null}var ADDRESS_RE=/^0x[0-9a-fA-F]{40}$/,HEX32_RE=/^0x[0-9a-fA-F]{64}$/;var X402_SESSION_USDC_BY_CHAIN=Object.freeze(new Map([["eip155:8453","0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"],["eip155:84532","0x036cbd53842c5426634e7929541ec2318f3dcf7e"]]));function x402SessionAssetCaip19(chain){let token=X402_SESSION_USDC_BY_CHAIN.get(chain);if(!token)return null;let asset=`${chain}/erc20:${token}`;return isCaip19(asset)?asset:null}function x402SessionAccountCaip10(chain,address){if(!isCaip2(chain)||typeof address!="string"||!ADDRESS_RE.test(address))return null;let account=`${chain}:${address.toLowerCase()}`;return isCaip10(account)?account:null}function x402SessionAccountSpellingIsUnpayable(account){let chain=caip2Of(account);if(chain===null||!isCaip10(account)||!X402_SESSION_USDC_BY_CHAIN.has(chain))return!1;let address=account.slice(chain.length+1);return x402SessionAccountCaip10(chain,address)!==account}var X402_SESSION_EVIDENCE_SCHEMA="voidly.session.settlement.x402/v1",EVIDENCE_KEYS=Object.freeze(["schema","transaction_hash"]),X402_SESSION_REFUSED_EVIDENCE_KEYS=Object.freeze(["authorization","signature","payload","payment","paymentPayload","payment_payload","paymentResponse","payment_response","settleResponse","settle_response","x402Version","receipt","facilitator"]);function validateX402SessionEvidence(raw){if(typeof raw!="object"||raw===null||Array.isArray(raw))return null;let e={...raw};for(let k of Object.keys(e))if(!EVIDENCE_KEYS.includes(k))return null;return e.schema!==X402_SESSION_EVIDENCE_SCHEMA||typeof e.transaction_hash!="string"||!HEX32_RE.test(e.transaction_hash)?null:Object.freeze({schema:X402_SESSION_EVIDENCE_SCHEMA,transaction_hash:e.transaction_hash.toLowerCase()})}var MAX_EVIDENCE_ID_LENGTH=128;var SETTLEMENT_BINDING_DOMAIN="voidly-session-settlement-binding/v1|";async function settlementBindingReference(grantHash){return sha256Hex(new TextEncoder().encode(SETTLEMENT_BINDING_DOMAIN+grantHash))}import __naclUtil3 from"tweetnacl-util";const{decodeBase64: decodeBase642}=__naclUtil3;var SESSION_HIRE_SCHEMA="voidly-session-hire/v1",SESSION_HIRE_ACCEPTED_SCHEMA="voidly-session-hire-accepted/v1",SESSION_HIRE_REFUSED_SCHEMA="voidly-session-hire-refused/v1",SESSION_PAYMENT_AUTHORIZATION_SCHEMA="voidly-session-payment-authorization/v1",PAYMENT_AUTHORIZATION_SCHEME="eip3009",AUTHORIZATION_ENTRY_POINTS=["transfer_with_authorization","receive_with_authorization"];function authorizationEntryPoint(authorization){return authorization.entry_point??"unstated"}var AUTHORIZATION_KEYS=["scheme","entry_point","chain","asset","from","to","value","valid_after","valid_before","nonce","signature"];var HIRE_ACCEPTED_KEYS=["schema","grant_hash","acceptance","acceptance_signature_base64"],HIRE_REFUSED_KEYS=["schema","reason","detail"],UNIX_SECONDS_RE=/^(0|[1-9][0-9]{0,19})$/,ECDSA_SIGNATURE_RE=/^0x[0-9a-fA-F]{130}$/;function isSignature64(value){if(typeof value!="string"||value.length===0)return!1;try{return decodeBase642(value).length===64}catch{return!1}}function validateAuthorizationShape(raw){if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"authorization_not_object"};let r={...raw};return hasOnlyKeys(r,AUTHORIZATION_KEYS)?r.scheme!==PAYMENT_AUTHORIZATION_SCHEME?{ok:!1,reason:"authorization_scheme_unsupported"}:r.entry_point!==void 0&&!AUTHORIZATION_ENTRY_POINTS.includes(r.entry_point)?{ok:!1,reason:"authorization_entry_point_unsupported"}:typeof r.chain!="string"||!isCaip2(r.chain)?{ok:!1,reason:"authorization_invalid_chain"}:typeof r.asset!="string"||!isCaip19(r.asset)||caip2Of(r.asset)!==r.chain?{ok:!1,reason:"authorization_invalid_asset"}:typeof r.from!="string"||!isCaip10(r.from)||caip2Of(r.from)!==r.chain?{ok:!1,reason:"authorization_invalid_from"}:typeof r.to!="string"||!isCaip10(r.to)||caip2Of(r.to)!==r.chain?{ok:!1,reason:"authorization_invalid_to"}:typeof r.value!="string"||!isPositiveDecimalString(r.value)?{ok:!1,reason:"authorization_invalid_value"}:typeof r.valid_after!="string"||!UNIX_SECONDS_RE.test(r.valid_after)?{ok:!1,reason:"authorization_invalid_valid_after"}:typeof r.valid_before!="string"||!UNIX_SECONDS_RE.test(r.valid_before)||r.valid_before==="0"?{ok:!1,reason:"authorization_invalid_valid_before"}:typeof r.nonce!="string"||!HEX64_RE.test(r.nonce)?{ok:!1,reason:"authorization_invalid_nonce"}:typeof r.signature!="string"||!ECDSA_SIGNATURE_RE.test(r.signature)?{ok:!1,reason:"authorization_invalid_signature"}:{ok:!0,env:{scheme:PAYMENT_AUTHORIZATION_SCHEME,...r.entry_point!==void 0?{entry_point:r.entry_point}:{},chain:r.chain,asset:r.asset,from:r.from,to:r.to,value:r.value,valid_after:r.valid_after,valid_before:r.valid_before,nonce:r.nonce,signature:r.signature}}:{ok:!1,reason:"authorization_unexpected_field"}}async function hireAuthorizationBinding(grantHash,authorization){let shape=validateAuthorizationShape(authorization);return shape.ok?{ok:!0,env:{schema:SESSION_PAYMENT_AUTHORIZATION_SCHEMA,grant_hash:grantHash,authorization_hash:await envelopeHash(shape.env)}}:{ok:!1,reason:shape.reason}}function authorizationValidBeforeFor(grant){let expiresMs=timestampMs(grant.expires_at);return expiresMs===null?null:String(Math.floor(expiresMs/1e3))}async function bindAuthorizationToGrant(authorization,grant,grantHash){if(authorization.chain!==grant.price_chain)return"authorization_chain_mismatch";if(authorization.asset!==grant.price_asset)return"authorization_asset_mismatch";if(authorization.from!==grant.price_payer_account)return"authorization_payer_mismatch";if(authorization.to!==grant.price_payee_account)return"authorization_payee_mismatch";if(x402SessionAccountSpellingIsUnpayable(grant.price_payer_account))return"grant_payer_account_not_canonical";if(x402SessionAccountSpellingIsUnpayable(grant.price_payee_account))return"grant_payee_account_not_canonical";let vsFloor=compareDecimalStrings(authorization.value,grant.price_min_amount);if(vsFloor===null||vsFloor<0)return"authorization_below_floor";let vsCeiling=compareDecimalStrings(authorization.value,grant.price_max_amount);if(vsCeiling===null||vsCeiling>0)return"authorization_over_ceiling";let expectedValidBefore=authorizationValidBeforeFor(grant);if(expectedValidBefore===null||authorization.valid_before!==expectedValidBefore)return"authorization_expiry_mismatch";let issuedMs=timestampMs(grant.issued_at);if(issuedMs===null)return"grant_invalid_timestamp";if(BigInt(authorization.valid_after)>BigInt(String(Math.floor(issuedMs/1e3))))return"authorization_valid_after_too_late";let expectedNonce=await settlementBindingReference(grantHash);return authorization.nonce!==expectedNonce?"authorization_binding_mismatch":null}function validateHireAccepted(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"clock_implausible"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"hire_not_object"};let r={...raw};if(r.schema!==SESSION_HIRE_ACCEPTED_SCHEMA)return{ok:!1,reason:"hire_schema_mismatch"};if(!hasOnlyKeys(r,HIRE_ACCEPTED_KEYS))return{ok:!1,reason:"hire_unexpected_field"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"acceptance_invalid_grant_hash"};let acceptance=validateAcceptance(r.acceptance,nowMs);return acceptance.ok?isSignature64(r.acceptance_signature_base64)?{ok:!0,env:{schema:SESSION_HIRE_ACCEPTED_SCHEMA,grant_hash:r.grant_hash,acceptance:acceptance.env,acceptance_signature_base64:r.acceptance_signature_base64}}:{ok:!1,reason:"invalid_acceptance_signature"}:{ok:!1,reason:acceptance.reason}}function verifyHireAcceptance(input){if(input.accepted.grant_hash!==input.grantHash||input.accepted.acceptance.grant_hash!==input.grantHash)return"acceptance_grant_mismatch";if(input.accepted.acceptance.redeemer_did!==input.grant.provider_did)return"acceptance_redeemer_mismatch";let providerKey;try{providerKey=decodeBase642(input.grant.provider_signing_pubkey_base64)}catch{return"grant_invalid_provider_signing_pubkey"}return providerKey.length!==32?"grant_invalid_provider_signing_pubkey":verifyDetached(input.accepted.acceptance,input.accepted.acceptance_signature_base64,providerKey)?null:"invalid_acceptance_signature"}function validateHireRefused(raw){if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"hire_not_object"};let r={...raw};if(r.schema!==SESSION_HIRE_REFUSED_SCHEMA)return{ok:!1,reason:"hire_schema_mismatch"};if(!hasOnlyKeys(r,HIRE_REFUSED_KEYS))return{ok:!1,reason:"hire_unexpected_field"};if(typeof r.reason!="string"||!HIRE_REFUSE_REASONS.includes(r.reason))return{ok:!1,reason:"hire_unexpected_field"};let detail=r.detail;return typeof detail!="string"||!/^[a-z0-9_]{1,64}$/.test(detail)?{ok:!1,reason:"hire_unexpected_field"}:{ok:!0,env:{schema:SESSION_HIRE_REFUSED_SCHEMA,reason:r.reason,detail}}}var HIRE_REFUSE_REASONS=["hire_malformed","identity_unresolved","hirer_key_mismatch","invalid_hirer_signature","artifact_binding_broken","not_the_named_provider","authorization_invalid","authorization_entry_point_refused","grant_already_committed","price_below_floor","window_too_short","brief_rejected","service_unavailable","ledger_unavailable"];async function buildHireMessage(wire,authorization,sign){let grantHash=await envelopeHash(wire.grant),binding=await hireAuthorizationBinding(grantHash,authorization);if(!binding.ok)return{ok:!1,reason:binding.reason};let signature=await signCanonical(binding.env,sign);return signature===null?{ok:!1,reason:"signer_failed"}:{ok:!0,env:{schema:SESSION_HIRE_SCHEMA,offer:wire.offer,offer_signature_base64:wire.offer_signature_base64,grant:wire.grant,grant_signature_base64:wire.grant_signature_base64,capsule:wire.capsule,authorization,authorization_signature_base64:signature}}}var HIRE_MEDIA_TYPE="application/json",MAX_HIRE_REQUEST_BYTES=512*1024,MAX_HIRE_RESPONSE_BYTES=16*1024,HIRE_REFUSAL_STATUS=Object.freeze({hire_malformed:400,invalid_hirer_signature:400,artifact_binding_broken:400,authorization_invalid:400,hirer_key_mismatch:400,window_too_short:400,price_below_floor:402,authorization_entry_point_refused:402,not_the_named_provider:403,grant_already_committed:409,identity_unresolved:422,brief_rejected:422,service_unavailable:503,ledger_unavailable:503});function isRetryableRefusal(reason){return HIRE_REFUSAL_STATUS[reason]>=500}var PAYMENT_STEERING_DERIVATION=Object.freeze({hire_malformed:{instrumentIsDead:!0,mechanism:null,because:"clause 2: accuses bytes the hirer built and can re-validate locally, and names no direction \u2014 clause 1 is `true` on the DETAIL TIE-BREAK, since `clock_implausible` is alive and `offer_invalid_hirer_pubkey` is dead"},identity_unresolved:{instrumentIsDead:!1,mechanism:null,because:"clause 1: register the DID, then send the IDENTICAL bytes \u2014 no wallet is asked"},hirer_key_mismatch:{instrumentIsDead:!1,mechanism:null,because:"clause 1: reconcile the registered signing key, then send the IDENTICAL bytes \u2014 the grant hash, the binding nonce and the authorization never move"},invalid_hirer_signature:{instrumentIsDead:!1,mechanism:null,because:"clause 1: envelopeHash has no signature in its preimage, so re-signing the same envelope leaves the grant hash and the authorization byte-identical"},artifact_binding_broken:{instrumentIsDead:!0,mechanism:null,because:"clause 2: every detail names the hirer's own artifacts contradicting each other \u2014 locally falsifiable, and no direction to move"},not_the_named_provider:{instrumentIsDead:!0,mechanism:null,because:"clause 2: contradicted by the SIGNED provider manifest the hirer already authenticated, so the replacement payee is the one it already chose"},authorization_invalid:{instrumentIsDead:!0,mechanism:"steer",because:"clause 2 steer: the direction rides in an attacker-chosen detail \u2014 below_floor walks `value` up, expiry_mismatch walks `valid_before` out"},authorization_entry_point_refused:{instrumentIsDead:!0,mechanism:"steer",because:"clause 2 steer: with two doors, 'not this one' is 'the bearer one' \u2014 more spendable; clause 1 is `true` on the DETAIL TIE-BREAK, since `..._unstated` is alive and `..._not_accepted` is dead"},grant_already_committed:{instrumentIsDead:!0,mechanism:"harvest",because:"clause 2 harvest: no direction, but whether the grant hash is taken is a fact about the provider's store that no artifact the hirer holds can contradict"},price_below_floor:{instrumentIsDead:!0,mechanism:"steer",because:"clause 2 steer: the word itself names the direction \u2014 more expensive"},window_too_short:{instrumentIsDead:!0,mechanism:"steer",because:"clause 2 steer: `valid_before` is derived from the grant's expiry, so a longer window is a longer-lived instrument beside the live one"},brief_rejected:{instrumentIsDead:!1,mechanism:null,because:"clause 1: the provider refused the WORK \u2014 no remedy satisfies it, so no wallet one"},service_unavailable:{instrumentIsDead:!1,mechanism:null,because:"clause 1: the IDENTICAL bytes, later \u2014 the defining property of a retryable refusal"},ledger_unavailable:{instrumentIsDead:!1,mechanism:null,because:"clause 1: the IDENTICAL bytes, later \u2014 the defining property of a retryable refusal"}}),PAYMENT_STEERING_REFUSALS=Object.freeze(HIRE_REFUSE_REASONS.filter(reason=>{let derivation=PAYMENT_STEERING_DERIVATION[reason];return derivation===void 0?!1:derivation.instrumentIsDead&&derivation.mechanism!==null}));function steersPayment(reason){return PAYMENT_STEERING_REFUSALS.includes(reason)}async function postHire(input){let response;try{response=await input.fetchImpl(input.url,{method:"POST",headers:{"content-type":HIRE_MEDIA_TYPE,accept:HIRE_MEDIA_TYPE},body:JSON.stringify(input.message),...input.signal?{signal:input.signal}:{}})}catch{return{kind:"undelivered",detail:"transport_failed"}}let text;try{text=await response.text()}catch{return{kind:"undelivered",detail:"response_body_unreadable"}}if(text.length>MAX_HIRE_RESPONSE_BYTES)return{kind:"unverifiable",detail:"response_malformed"};let body;try{body=JSON.parse(text)}catch{return response.status>=500?{kind:"undelivered",detail:"response_not_json"}:{kind:"unverifiable",detail:"response_malformed"}}if(response.status===200){let parsed=validateHireAccepted(body,input.nowMs);if(!parsed.ok)return{kind:"unverifiable",detail:parsed.reason};let fault=verifyHireAcceptance({accepted:parsed.env,grant:input.grant,grantHash:input.grantHash});return fault!==null?{kind:"unverifiable",detail:fault}:{kind:"accepted",accepted:parsed.env}}let refused=validateHireRefused(body);return refused.ok?{kind:"refused",refused:refused.env,retryable:isRetryableRefusal(refused.env.reason),steersPayment:steersPayment(refused.env.reason)}:response.status>=500?{kind:"undelivered",detail:"response_not_a_refusal"}:{kind:"unverifiable",detail:refused.reason}}import __naclUtil4 from"tweetnacl-util";const{decodeBase64: decodeBase643}=__naclUtil4;function validateOffer(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"clock_implausible"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"offer_not_object"};let r={...raw};if(r.schema!==SESSION_OFFER_SCHEMA)return{ok:!1,reason:"offer_schema_mismatch"};if(!hasOnlyKeys(r,OFFER_KEYS))return{ok:!1,reason:"offer_unexpected_field"};if(typeof r.hirer_did!="string"||!DID_RE.test(r.hirer_did))return{ok:!1,reason:"offer_invalid_hirer_did"};if(!isBase64Key32(r.hirer_signing_pubkey_base64,decodeBase643))return{ok:!1,reason:"offer_invalid_hirer_pubkey"};if(typeof r.provider_did!="string"||!DID_RE.test(r.provider_did))return{ok:!1,reason:"offer_invalid_provider_did"};if(r.hirer_did===r.provider_did)return{ok:!1,reason:"offer_self_hire_not_allowed"};if(typeof r.service_ref!="string"||r.service_ref.length<1||r.service_ref.length>MAX_SERVICE_REF_LENGTH)return{ok:!1,reason:"offer_invalid_service_ref"};if(typeof r.price_chain!="string"||!isCaip2(r.price_chain))return{ok:!1,reason:"offer_invalid_price_chain"};if(typeof r.price_asset!="string"||!isCaip19(r.price_asset))return{ok:!1,reason:"offer_invalid_price_asset"};if(typeof r.price_payer_account!="string"||!isCaip10(r.price_payer_account))return{ok:!1,reason:"offer_invalid_price_payer_account"};if(typeof r.price_payee_account!="string"||!isCaip10(r.price_payee_account))return{ok:!1,reason:"offer_invalid_price_payee_account"};if(caip2Of(r.price_asset)!==r.price_chain||caip2Of(r.price_payer_account)!==r.price_chain||caip2Of(r.price_payee_account)!==r.price_chain)return{ok:!1,reason:"offer_price_chain_mismatch"};if(typeof r.price_max_amount!="string"||!isPositiveDecimalString(r.price_max_amount))return{ok:!1,reason:"offer_invalid_price_amount"};if(typeof r.price_min_amount!="string"||!isPositiveDecimalString(r.price_min_amount))return{ok:!1,reason:"offer_invalid_price_min_amount"};let band=compareDecimalStrings(r.price_min_amount,r.price_max_amount);if(band===null||band>0)return{ok:!1,reason:"offer_price_band_inverted"};if(!isNonce(r.nonce,MIN_NONCE_LENGTH))return{ok:!1,reason:"offer_invalid_nonce"};let issuedAt=timestampMs(r.issued_at),expiresAt=timestampMs(r.expires_at);if(issuedAt===null||expiresAt===null)return{ok:!1,reason:"offer_invalid_timestamp"};let window=expiresAt-issuedAt;return window<=0?{ok:!1,reason:"offer_invalid_timestamp"}:window>MAX_OFFER_TTL_MS?{ok:!1,reason:"offer_ttl_too_long"}:nowMs<issuedAt-MAX_CLOCK_SKEW_MS?{ok:!1,reason:"offer_not_yet_valid"}:{ok:!0,env:{schema:SESSION_OFFER_SCHEMA,hirer_did:r.hirer_did,hirer_signing_pubkey_base64:r.hirer_signing_pubkey_base64,provider_did:r.provider_did,service_ref:r.service_ref,price_chain:r.price_chain,price_asset:r.price_asset,price_payer_account:r.price_payer_account,price_payee_account:r.price_payee_account,price_min_amount:r.price_min_amount,price_max_amount:r.price_max_amount,nonce:r.nonce,issued_at:r.issued_at,expires_at:r.expires_at}}}import __naclUtil5 from"tweetnacl-util";const{decodeBase64: decodeBase644}=__naclUtil5;function providerIdentityBindingFailure(providerDid,providerSigningPubkeyBase64){if(typeof providerDid!="string"||!DID_RE.test(providerDid))return"grant_invalid_provider_did";if(!isBase64Key32(providerSigningPubkeyBase64,decodeBase644))return"grant_invalid_provider_signing_pubkey";let derived=null;try{derived=deriveDidFromSigningKey(decodeBase644(providerSigningPubkeyBase64))}catch{derived=null}return derived===null||derived!==providerDid?"provider_key_mismatch":null}function validateGrant(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"clock_implausible"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"grant_not_object"};let r={...raw};if(r.schema!==TASK_GRANT_SCHEMA)return{ok:!1,reason:"grant_schema_mismatch"};if(!hasOnlyKeys(r,GRANT_KEYS))return{ok:!1,reason:"grant_unexpected_field"};if(typeof r.hirer_did!="string"||!DID_RE.test(r.hirer_did))return{ok:!1,reason:"grant_invalid_hirer_did"};if(typeof r.provider_did!="string"||!DID_RE.test(r.provider_did))return{ok:!1,reason:"grant_invalid_provider_did"};if(r.hirer_did===r.provider_did)return{ok:!1,reason:"grant_self_hire_not_allowed"};if(!isBase64Key32(r.provider_signing_pubkey_base64,decodeBase644))return{ok:!1,reason:"grant_invalid_provider_signing_pubkey"};if(!isBase64Key32(r.provider_enc_pubkey_base64,decodeBase644))return{ok:!1,reason:"grant_invalid_provider_enc_pubkey"};let providerBinding=providerIdentityBindingFailure(r.provider_did,r.provider_signing_pubkey_base64);if(providerBinding!==null)return{ok:!1,reason:providerBinding};if(typeof r.offer_hash!="string"||!HEX64_RE.test(r.offer_hash))return{ok:!1,reason:"grant_invalid_offer_hash"};if(typeof r.capsule_hash!="string"||!HEX64_RE.test(r.capsule_hash))return{ok:!1,reason:"grant_invalid_capsule_hash"};if(typeof r.brief_commitment!="string"||!HEX64_RE.test(r.brief_commitment))return{ok:!1,reason:"grant_invalid_brief_commitment"};if(typeof r.price_chain!="string"||!isCaip2(r.price_chain))return{ok:!1,reason:"grant_invalid_price_chain"};if(typeof r.price_asset!="string"||!isCaip19(r.price_asset))return{ok:!1,reason:"grant_invalid_price_asset"};if(typeof r.price_payer_account!="string"||!isCaip10(r.price_payer_account))return{ok:!1,reason:"grant_invalid_price_payer_account"};if(typeof r.price_payee_account!="string"||!isCaip10(r.price_payee_account))return{ok:!1,reason:"grant_invalid_price_payee_account"};if(caip2Of(r.price_asset)!==r.price_chain||caip2Of(r.price_payer_account)!==r.price_chain||caip2Of(r.price_payee_account)!==r.price_chain)return{ok:!1,reason:"grant_price_chain_mismatch"};if(typeof r.price_max_amount!="string"||!isPositiveDecimalString(r.price_max_amount))return{ok:!1,reason:"grant_invalid_price_amount"};if(typeof r.price_min_amount!="string"||!isPositiveDecimalString(r.price_min_amount))return{ok:!1,reason:"grant_invalid_price_min_amount"};let band=compareDecimalStrings(r.price_min_amount,r.price_max_amount);if(band===null||band>0)return{ok:!1,reason:"grant_price_band_inverted"};if(!isNonce(r.nonce,MIN_NONCE_LENGTH))return{ok:!1,reason:"grant_invalid_nonce"};let issuedAt=timestampMs(r.issued_at),expiresAt=timestampMs(r.expires_at);if(issuedAt===null||expiresAt===null)return{ok:!1,reason:"grant_invalid_timestamp"};let window=expiresAt-issuedAt;return window<=0?{ok:!1,reason:"grant_invalid_timestamp"}:window>MAX_GRANT_TTL_MS?{ok:!1,reason:"grant_ttl_too_long"}:window<MIN_GRANT_TTL_MS?{ok:!1,reason:"grant_ttl_below_settlement_depth"}:nowMs<issuedAt-MAX_CLOCK_SKEW_MS?{ok:!1,reason:"grant_not_yet_valid"}:{ok:!0,env:{schema:TASK_GRANT_SCHEMA,hirer_did:r.hirer_did,provider_did:r.provider_did,provider_signing_pubkey_base64:r.provider_signing_pubkey_base64,provider_enc_pubkey_base64:r.provider_enc_pubkey_base64,offer_hash:r.offer_hash,capsule_hash:r.capsule_hash,brief_commitment:r.brief_commitment,price_chain:r.price_chain,price_asset:r.price_asset,price_payer_account:r.price_payer_account,price_payee_account:r.price_payee_account,price_min_amount:r.price_min_amount,price_max_amount:r.price_max_amount,nonce:r.nonce,issued_at:r.issued_at,expires_at:r.expires_at}}}import nacl2 from"tweetnacl";import __naclUtil6 from"tweetnacl-util";const{decodeBase64: decodeBase645, encodeBase64: encodeBase642}=__naclUtil6;var REDEMPTION_ATTESTATION_SCHEMA="voidly-session-redemption-attestation/v1",REDEMPTION_ATTESTATION_KEYS=["schema","grant_hash","capsule_hash","offer_hash","hirer_did","provider_did","evidence_id","settled_chain","settled_asset","settled_amount","redeemed_at","expires_at"];function validateRedemptionAttestation(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"open_clock_implausible"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"attestation_not_object"};let r={...raw};if(r.schema!==REDEMPTION_ATTESTATION_SCHEMA)return{ok:!1,reason:"attestation_schema_mismatch"};for(let key of Object.keys(r))if(!REDEMPTION_ATTESTATION_KEYS.includes(key))return{ok:!1,reason:"attestation_unexpected_field"};if(Object.keys(r).length!==REDEMPTION_ATTESTATION_KEYS.length)return{ok:!1,reason:"attestation_unexpected_field"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"attestation_invalid_grant_hash"};if(typeof r.capsule_hash!="string"||!HEX64_RE.test(r.capsule_hash))return{ok:!1,reason:"attestation_invalid_capsule_hash"};if(typeof r.offer_hash!="string"||!HEX64_RE.test(r.offer_hash))return{ok:!1,reason:"attestation_invalid_offer_hash"};if(typeof r.hirer_did!="string"||!DID_RE.test(r.hirer_did))return{ok:!1,reason:"attestation_invalid_hirer_did"};if(typeof r.provider_did!="string"||!DID_RE.test(r.provider_did))return{ok:!1,reason:"attestation_invalid_provider_did"};if(typeof r.evidence_id!="string"||r.evidence_id.length===0||r.evidence_id.length>MAX_EVIDENCE_ID_LENGTH)return{ok:!1,reason:"attestation_invalid_evidence_id"};if(typeof r.settled_chain!="string"||!isCaip2(r.settled_chain))return{ok:!1,reason:"attestation_invalid_settled_chain"};if(typeof r.settled_asset!="string"||!isCaip19(r.settled_asset))return{ok:!1,reason:"attestation_invalid_settled_asset"};if(typeof r.settled_amount!="string"||!isPositiveDecimalString(r.settled_amount))return{ok:!1,reason:"attestation_invalid_settled_amount"};let redeemedAt=timestampMs(r.redeemed_at),expiresAt=timestampMs(r.expires_at);return redeemedAt===null||expiresAt===null?{ok:!1,reason:"attestation_invalid_timestamp"}:expiresAt<redeemedAt?{ok:!1,reason:"attestation_invalid_timestamp"}:{ok:!0,env:{schema:REDEMPTION_ATTESTATION_SCHEMA,grant_hash:r.grant_hash,capsule_hash:r.capsule_hash,offer_hash:r.offer_hash,hirer_did:r.hirer_did,provider_did:r.provider_did,evidence_id:r.evidence_id,settled_chain:r.settled_chain,settled_asset:r.settled_asset,settled_amount:r.settled_amount,redeemed_at:r.redeemed_at,expires_at:r.expires_at}}}var FRAME_LENGTH_OFFSET=FRAME_OFFER_HASH_BYTES+FRAME_BODY_NONCE_BYTES;function hex64ToBytes(hex){if(typeof hex!="string"||!HEX64_RE.test(hex))return null;let out=new Uint8Array(FRAME_OFFER_HASH_BYTES);for(let i=0;i<FRAME_OFFER_HASH_BYTES;i++)out[i]=Number.parseInt(hex.slice(i*2,i*2+2),16);return out}function viewOf(bytes){return new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength)}function frameSealedPayload(payload,offerHash,bodyNonce){let bound=hex64ToBytes(offerHash);if(!bound||!(bodyNonce instanceof Uint8Array)||bodyNonce.length!==FRAME_BODY_NONCE_BYTES)return null;let total=FRAME_HEADER_LENGTH+payload.length;if(frameBucketSize(total)>MAX_FRAME_BUCKET_BYTES)return null;let framed=new Uint8Array(frameBucketSize(total));return framed.set(bound,0),framed.set(bodyNonce,FRAME_OFFER_HASH_BYTES),viewOf(framed).setUint32(FRAME_LENGTH_OFFSET,payload.length,!1),framed.set(payload,FRAME_HEADER_LENGTH),framed}function unframeSealedPayload(framed,offerHash,bodyNonce){if(!(framed instanceof Uint8Array)||framed.length<FRAME_HEADER_LENGTH)return null;let bound=hex64ToBytes(offerHash);if(!bound||!(bodyNonce instanceof Uint8Array)||bodyNonce.length!==FRAME_BODY_NONCE_BYTES)return null;let diff=0;for(let i=0;i<FRAME_OFFER_HASH_BYTES;i++)diff|=framed[i]^bound[i];for(let i=0;i<FRAME_BODY_NONCE_BYTES;i++)diff|=framed[FRAME_OFFER_HASH_BYTES+i]^bodyNonce[i];if(diff!==0)return null;let length=viewOf(framed).getUint32(FRAME_LENGTH_OFFSET,!1),total=FRAME_HEADER_LENGTH+length;if(framed.length!==frameBucketSize(total)||framed.length>MAX_FRAME_BUCKET_BYTES)return null;for(let i=total;i<framed.length;i++)if(framed[i]!==0)return null;return framed.slice(FRAME_HEADER_LENGTH,total)}var REDACTED="[redacted:session-key]";var INSPECT_CUSTOM=Symbol.for("nodejs.util.inspect.custom"),_isSessionKey,SessionKeyHandle=class{constructor(){__privateAdd(this,_isSessionKey,!0)}toJSON(){return REDACTED}toString(){return REDACTED}[INSPECT_CUSTOM](){return REDACTED}get isSessionKey(){return __privateGet(this,_isSessionKey)}};_isSessionKey=new WeakMap;var SESSION_KEY_BYTES=new WeakMap;function importSessionKey(bytes){if(!(bytes instanceof Uint8Array)||bytes.length!==32)throw new Error("importSessionKey: session key must be 32 bytes");let handle=new SessionKeyHandle;return SESSION_KEY_BYTES.set(handle,Uint8Array.from(bytes)),handle}function exportSessionKeyBytes(key){let bytes=SESSION_KEY_BYTES.get(key);return bytes?Uint8Array.from(bytes):null}function destroySessionKey(key){let bytes=SESSION_KEY_BYTES.get(key);bytes&&bytes.fill(0),SESSION_KEY_BYTES.delete(key)}var SALT_LENGTH=32,WRAPPED_KEY_LENGTH=48;function briefPayloadBytes(brief,briefSalt){return canonicalBytes({schema:TASK_BRIEF_SCHEMA,brief,salt_base64:encodeBase642(briefSalt)})}function sealedPayloadFitsTransport(payload){return frameBucketSize(FRAME_HEADER_LENGTH+payload.length)<=MAX_FRAME_BUCKET_BYTES}function decodeNonce(base64){if(typeof base64!="string"||base64.length===0)return null;let bytes;try{bytes=decodeBase645(base64)}catch{return null}return bytes.length!==24||isAllZero(bytes)?null:bytes}function validateCapsuleShape(raw){if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"capsule_not_object"};let r={...raw};if(r.schema!==TASK_CAPSULE_SCHEMA)return{ok:!1,reason:"capsule_schema_mismatch"};if(!hasOnlyKeys(r,CAPSULE_KEYS))return{ok:!1,reason:"capsule_unexpected_field"};if(r.alg!==CAPSULE_ALG)return{ok:!1,reason:"capsule_invalid_alg"};if(typeof r.offer_hash!="string"||!HEX64_RE.test(r.offer_hash))return{ok:!1,reason:"capsule_invalid_offer_hash"};if(!isBase64Key32(r.recipient_enc_pubkey_base64,decodeBase645))return{ok:!1,reason:"capsule_invalid_recipient_pubkey"};if(!isBase64Key32(r.ephemeral_pubkey_base64,decodeBase645))return{ok:!1,reason:"capsule_invalid_ephemeral_pubkey"};if(typeof r.wrapped_session_key_base64!="string")return{ok:!1,reason:"capsule_invalid_wrapped_key"};try{if(decodeBase645(r.wrapped_session_key_base64).length!==WRAPPED_KEY_LENGTH)return{ok:!1,reason:"capsule_invalid_wrapped_key"}}catch{return{ok:!1,reason:"capsule_invalid_wrapped_key"}}if(!decodeNonce(r.wrap_nonce_base64))return{ok:!1,reason:"capsule_invalid_wrap_nonce"};if(!decodeNonce(r.body_nonce_base64))return{ok:!1,reason:"capsule_invalid_body_nonce"};if(typeof r.body_base64!="string"||r.body_base64.length===0)return{ok:!1,reason:"capsule_invalid_body"};if(r.body_base64.length>MAX_CAPSULE_BODY_BASE64_LENGTH)return{ok:!1,reason:"capsule_body_too_large"};try{if(decodeBase645(r.body_base64).length<16)return{ok:!1,reason:"capsule_invalid_body"}}catch{return{ok:!1,reason:"capsule_invalid_body"}}return{ok:!0,env:{schema:TASK_CAPSULE_SCHEMA,alg:CAPSULE_ALG,offer_hash:r.offer_hash,recipient_enc_pubkey_base64:r.recipient_enc_pubkey_base64,ephemeral_pubkey_base64:r.ephemeral_pubkey_base64,wrapped_session_key_base64:r.wrapped_session_key_base64,wrap_nonce_base64:r.wrap_nonce_base64,body_nonce_base64:r.body_nonce_base64,body_base64:r.body_base64}}}async function sealCapsule(input){if(input.sessionKeyBytes.length!==32)throw new Error("sealCapsule: session key must be 32 bytes");if(isAllZero(input.sessionKeyBytes))throw new Error("sealCapsule: session key must not be all zero");if(input.ephemeralSecretKey.length!==32)throw new Error("sealCapsule: ephemeral secret key must be 32 bytes");if(isAllZero(input.ephemeralSecretKey))throw new Error("sealCapsule: ephemeral secret key must not be all zero");if(input.briefSalt.length!==SALT_LENGTH)throw new Error("sealCapsule: brief salt must be 32 bytes");if(isAllZero(input.briefSalt))throw new Error("sealCapsule: brief salt must not be all zero");if(input.recipientEncPublicKey.length!==32)throw new Error("sealCapsule: recipient encryption key must be 32 bytes");if(input.bodyNonce.length!==24||isAllZero(input.bodyNonce))throw new Error("sealCapsule: body nonce must be 24 non-zero bytes");if(input.wrapNonce.length!==24||isAllZero(input.wrapNonce))throw new Error("sealCapsule: wrap nonce must be 24 non-zero bytes");if(!HEX64_RE.test(input.offerHash))throw new Error("sealCapsule: offer hash must be 64-char lowercase hex");let payload=briefPayloadBytes(input.brief,input.briefSalt),sealedBytes=frameSealedPayload(payload,input.offerHash,input.bodyNonce);if(!sealedBytes)throw new Error("sealCapsule: could not frame the payload");let briefCommitment=await sha256Hex(sealedBytes),body=nacl2.secretbox(sealedBytes,input.bodyNonce,input.sessionKeyBytes),ephemeralPublicKey=nacl2.box.keyPair.fromSecretKey(input.ephemeralSecretKey).publicKey,wrapped=nacl2.box(input.sessionKeyBytes,input.wrapNonce,input.recipientEncPublicKey,input.ephemeralSecretKey);return{capsule:{schema:TASK_CAPSULE_SCHEMA,alg:CAPSULE_ALG,offer_hash:input.offerHash,recipient_enc_pubkey_base64:encodeBase642(input.recipientEncPublicKey),ephemeral_pubkey_base64:encodeBase642(ephemeralPublicKey),wrapped_session_key_base64:encodeBase642(wrapped),wrap_nonce_base64:encodeBase642(input.wrapNonce),body_nonce_base64:encodeBase642(input.bodyNonce),body_base64:encodeBase642(body)},sessionKey:importSessionKey(input.sessionKeyBytes),briefCommitment}}async function decryptCapsule(c,g,recipientEncSecretKey){try{if(recipientEncSecretKey.length!==32)return{kind:"unopenable"};let ephemeralPublicKey=decodeBase645(c.ephemeral_pubkey_base64),wrapNonce=decodeNonce(c.wrap_nonce_base64);if(!wrapNonce)return{kind:"unopenable"};let sessionKeyBytes=nacl2.box.open(decodeBase645(c.wrapped_session_key_base64),wrapNonce,ephemeralPublicKey,recipientEncSecretKey);if(!sessionKeyBytes||sessionKeyBytes.length!==32)return{kind:"unopenable"};let sessionKey=importSessionKey(sessionKeyBytes),opened=await unsealBody(c,sessionKey);if(opened.kind!=="opened")return{kind:"unopenable"};if(await sha256Hex(opened.bytes)!==g.brief_commitment)return{kind:"unopenable"};let parsed=JSON.parse(new TextDecoder().decode(opened.payload));return parsed.schema!==TASK_BRIEF_SCHEMA||typeof parsed.brief!="string"?{kind:"unopenable"}:{kind:"opened",brief:parsed.brief,sessionKey}}catch{return{kind:"unopenable"}}}async function openCapsuleAsProvider(input){try{let attCheck=validateRedemptionAttestation(input.attestation,input.nowMs);if(!attCheck.ok)return{kind:"refused",reason:attCheck.reason};let att=attCheck.env;if(input.attestorSigningPublicKey.length!==32)return{kind:"refused",reason:"attestor_key_invalid"};if(!verifyDetached(att,input.attestationSignatureBase64,input.attestorSigningPublicKey))return{kind:"refused",reason:"attestation_invalid_signature"};let grantCheck=validateGrant(input.grant,input.nowMs);if(!grantCheck.ok)return{kind:"refused",reason:"grant_unreadable"};let g=grantCheck.env;if(input.hirerSigningPublicKey.length!==32)return{kind:"refused",reason:"hirer_key_invalid"};if(deriveDidFromSigningKey(input.hirerSigningPublicKey)!==g.hirer_did)return{kind:"refused",reason:"hirer_key_not_derivable"};if(!verifyDetached(g,input.grantSignatureBase64,input.hirerSigningPublicKey))return{kind:"refused",reason:"invalid_grant_signature"};let capCheck=validateCapsuleShape(input.capsule);if(!capCheck.ok)return{kind:"refused",reason:"capsule_unreadable"};let c=capCheck.env,grantHash=await envelopeHash(g),capsuleHash=await envelopeHash(c);if(att.grant_hash!==grantHash)return{kind:"refused",reason:"attestation_grant_mismatch"};if(att.capsule_hash!==capsuleHash)return{kind:"refused",reason:"attestation_capsule_mismatch"};if(g.capsule_hash!==capsuleHash)return{kind:"refused",reason:"capsule_binding_mismatch"};if(att.offer_hash!==g.offer_hash)return{kind:"refused",reason:"attestation_offer_mismatch"};if(c.offer_hash!==g.offer_hash)return{kind:"refused",reason:"capsule_binding_mismatch"};if(att.hirer_did!==g.hirer_did)return{kind:"refused",reason:"attestation_hirer_mismatch"};if(att.provider_did!==g.provider_did)return{kind:"refused",reason:"attestation_provider_mismatch"};if(g.provider_did!==input.providerDid)return{kind:"refused",reason:"provider_did_mismatch"};if(c.recipient_enc_pubkey_base64!==g.provider_enc_pubkey_base64)return{kind:"refused",reason:"recipient_binding_mismatch"};if(att.settled_chain!==g.price_chain||att.settled_asset!==g.price_asset)return{kind:"refused",reason:"attestation_settlement_mismatch"};let belowFloor=compareDecimalStrings(att.settled_amount,g.price_min_amount);if(belowFloor===null||belowFloor<0)return{kind:"refused",reason:"attestation_below_agreed_price"};let grantExpiresMs=timestampMs(g.expires_at),attExpiresMs=timestampMs(att.expires_at),attIssuedMs=timestampMs(att.redeemed_at);return grantExpiresMs===null||attExpiresMs===null||attIssuedMs===null?{kind:"refused",reason:"attestation_invalid_timestamp"}:attExpiresMs>grantExpiresMs?{kind:"refused",reason:"attestation_outlives_grant"}:input.nowMs<attIssuedMs-MAX_CLOCK_SKEW_MS?{kind:"refused",reason:"attestation_not_yet_valid"}:input.nowMs>attExpiresMs?{kind:"refused",reason:"attestation_expired"}:await decryptCapsule(c,g,input.recipientEncSecretKey)}catch{return{kind:"unopenable"}}}async function unsealBody(capsule,sessionKey){try{let keyBytes=exportSessionKeyBytes(sessionKey);if(!keyBytes||keyBytes.length!==32)return{kind:"unopenable"};let bodyNonce=decodeNonce(capsule.body_nonce_base64);if(!bodyNonce)return{kind:"unopenable"};let opened=nacl2.secretbox.open(decodeBase645(capsule.body_base64),bodyNonce,keyBytes);if(!opened)return{kind:"unopenable"};let payload=unframeSealedPayload(opened,capsule.offer_hash,bodyNonce);return payload?{kind:"opened",bytes:opened,payload}:{kind:"unopenable"}}catch{return{kind:"unopenable"}}}import nacl3 from"tweetnacl";import __naclUtil7 from"tweetnacl-util";const{decodeBase64: decodeBase646, encodeBase64: encodeBase643}=__naclUtil7;var SALT_LENGTH2=32;function resultPayloadBytes(result2,resultSalt){return canonicalBytes({schema:TASK_RESULT_SCHEMA,result:result2,salt_base64:encodeBase643(resultSalt)})}var MEASUREMENT_SALT=new Uint8Array(SALT_LENGTH2);function decodeNonce2(base64){if(typeof base64!="string"||base64.length===0)return null;let bytes;try{bytes=decodeBase646(base64)}catch{return null}return bytes.length!==24||isAllZero(bytes)?null:bytes}function sameBytes(a,b){if(a.length!==b.length)return!1;let diff=0;for(let i=0;i<a.length;i++)diff|=a[i]^b[i];return diff===0}function validateResultCapsuleShape(raw){if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"result_capsule_not_object"};let r={...raw};if(r.schema!==TASK_RESULT_CAPSULE_SCHEMA)return{ok:!1,reason:"result_capsule_schema_mismatch"};if(!hasOnlyKeys(r,RESULT_CAPSULE_KEYS))return{ok:!1,reason:"result_capsule_unexpected_field"};if(r.alg!==RESULT_CAPSULE_ALG)return{ok:!1,reason:"result_capsule_invalid_alg"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"result_capsule_invalid_grant_hash"};if(!decodeNonce2(r.body_nonce_base64))return{ok:!1,reason:"result_capsule_invalid_body_nonce"};if(typeof r.body_base64!="string"||r.body_base64.length===0)return{ok:!1,reason:"result_capsule_invalid_body"};if(r.body_base64.length>MAX_RESULT_BODY_BASE64_LENGTH)return{ok:!1,reason:"result_body_too_large"};try{if(decodeBase646(r.body_base64).length<16)return{ok:!1,reason:"result_capsule_invalid_body"}}catch{return{ok:!1,reason:"result_capsule_invalid_body"}}return{ok:!0,env:{schema:TASK_RESULT_CAPSULE_SCHEMA,alg:RESULT_CAPSULE_ALG,grant_hash:r.grant_hash,body_nonce_base64:r.body_nonce_base64,body_base64:r.body_base64}}}async function sealResult(input){if(typeof input.result!="string"||input.result.length>MAX_RESULT_LENGTH)throw new Error("sealResult: result is too long");if(!HEX64_RE.test(input.grantHash))throw new Error("sealResult: grant hash must be 64-char lowercase hex");if(!(input.resultSalt instanceof Uint8Array)||input.resultSalt.length!==SALT_LENGTH2)throw new Error("sealResult: result salt must be 32 bytes");if(isAllZero(input.resultSalt))throw new Error("sealResult: result salt must not be all zero");if(!(input.bodyNonce instanceof Uint8Array)||input.bodyNonce.length!==24||isAllZero(input.bodyNonce))throw new Error("sealResult: body nonce must be 24 non-zero bytes");if(!(input.briefBodyNonce instanceof Uint8Array)||input.briefBodyNonce.length!==24)throw new Error("sealResult: the brief body nonce must be 24 bytes");if(sameBytes(input.bodyNonce,input.briefBodyNonce))throw new Error("sealResult: body nonce repeats the brief capsule's \u2014 a two-time pad");let keyBytes=exportSessionKeyBytes(input.sessionKey);if(!keyBytes||keyBytes.length!==32)throw new Error("sealResult: session key is unavailable");let payload=resultPayloadBytes(input.result,input.resultSalt);if(!sealedPayloadFitsTransport(payload))throw new Error("sealResult: the sealed result exceeds the session transport ceiling");let sealedBytes=frameSealedPayload(payload,input.grantHash,input.bodyNonce);if(!sealedBytes)throw new Error("sealResult: could not frame the payload");let resultCommitment=await sha256Hex(sealedBytes),body=nacl3.secretbox(sealedBytes,input.bodyNonce,keyBytes);return{capsule:{schema:TASK_RESULT_CAPSULE_SCHEMA,alg:RESULT_CAPSULE_ALG,grant_hash:input.grantHash,body_nonce_base64:encodeBase643(input.bodyNonce),body_base64:encodeBase643(body)},resultCommitment}}async function openResult(capsule,sessionKey,grantHash,expectedCommitment){try{let shape=validateResultCapsuleShape(capsule);if(!shape.ok)return{kind:"unopenable"};let c=shape.env;if(!HEX64_RE.test(grantHash)||c.grant_hash!==grantHash)return{kind:"unopenable"};if(!HEX64_RE.test(expectedCommitment))return{kind:"unopenable"};let keyBytes=exportSessionKeyBytes(sessionKey);if(!keyBytes||keyBytes.length!==32)return{kind:"unopenable"};let bodyNonce=decodeNonce2(c.body_nonce_base64);if(!bodyNonce)return{kind:"unopenable"};let opened=nacl3.secretbox.open(decodeBase646(c.body_base64),bodyNonce,keyBytes);if(!opened)return{kind:"unopenable"};let payload=unframeSealedPayload(opened,grantHash,bodyNonce);if(!payload)return{kind:"unopenable"};if(await sha256Hex(opened)!==expectedCommitment)return{kind:"unopenable"};let parsed=JSON.parse(new TextDecoder().decode(payload));return parsed.schema!==TASK_RESULT_SCHEMA||typeof parsed.result!="string"?{kind:"unopenable"}:{kind:"opened",result:parsed.result}}catch{return{kind:"unopenable"}}}function validateDeliveryReceipt(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"delivery_invalid_timestamp"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"delivery_not_object"};let r={...raw};if(r.schema!==TASK_DELIVERY_SCHEMA)return{ok:!1,reason:"delivery_schema_mismatch"};if(!hasOnlyKeys(r,DELIVERY_KEYS))return{ok:!1,reason:"delivery_unexpected_field"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"delivery_invalid_grant_hash"};if(typeof r.offer_hash!="string"||!HEX64_RE.test(r.offer_hash))return{ok:!1,reason:"delivery_invalid_offer_hash"};if(typeof r.provider_did!="string"||!DID_RE.test(r.provider_did))return{ok:!1,reason:"delivery_invalid_provider_did"};if(typeof r.result_capsule_hash!="string"||!HEX64_RE.test(r.result_capsule_hash))return{ok:!1,reason:"delivery_invalid_result_capsule_hash"};if(typeof r.result_commitment!="string"||!HEX64_RE.test(r.result_commitment))return{ok:!1,reason:"delivery_invalid_result_commitment"};let issuedAt=timestampMs(r.issued_at),recoverableUntil=timestampMs(r.recoverable_until);if(issuedAt===null||recoverableUntil===null)return{ok:!1,reason:"delivery_invalid_timestamp"};let window=recoverableUntil-issuedAt;return window<=0?{ok:!1,reason:"delivery_invalid_timestamp"}:window>MAX_RECOVERY_TTL_MS+MAX_GRANT_TTL_MS?{ok:!1,reason:"delivery_window_too_long"}:nowMs<issuedAt-MAX_CLOCK_SKEW_MS?{ok:!1,reason:"delivery_not_yet_valid"}:{ok:!0,env:{schema:TASK_DELIVERY_SCHEMA,grant_hash:r.grant_hash,offer_hash:r.offer_hash,provider_did:r.provider_did,result_capsule_hash:r.result_capsule_hash,result_commitment:r.result_commitment,issued_at:r.issued_at,recoverable_until:r.recoverable_until}}}async function buildDeliveryReceipt(input){if(!isPlausibleNowMs(input.recoverableUntilMs))return{ok:!1,reason:"delivery_invalid_timestamp"};let receipt={schema:TASK_DELIVERY_SCHEMA,grant_hash:input.grantHash,offer_hash:input.offerHash,provider_did:input.providerDid,result_capsule_hash:input.resultCapsuleHash,result_commitment:input.resultCommitment,issued_at:new Date(input.nowMs).toISOString(),recoverable_until:new Date(input.recoverableUntilMs).toISOString()},check=validateDeliveryReceipt(receipt,input.nowMs);if(!check.ok)return{ok:!1,reason:check.reason};let signature=await signCanonical(check.env,input.sign);return signature===null?{ok:!1,reason:"invalid_delivery_signature"}:{ok:!0,receipt:check.env,signature_base64:signature}}function validateRecoveryRequest(raw,nowMs){if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"recovery_invalid_timestamp"};if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"recovery_not_object"};let r={...raw};if(r.schema!==TASK_RECOVERY_SCHEMA)return{ok:!1,reason:"recovery_schema_mismatch"};if(!hasOnlyKeys(r,RECOVERY_KEYS))return{ok:!1,reason:"recovery_unexpected_field"};if(typeof r.grant_hash!="string"||!HEX64_RE.test(r.grant_hash))return{ok:!1,reason:"recovery_invalid_grant_hash"};if(typeof r.requester_did!="string"||!DID_RE.test(r.requester_did))return{ok:!1,reason:"recovery_invalid_requester_did"};if(!isNonce(r.action_nonce,MIN_NONCE_LENGTH))return{ok:!1,reason:"recovery_invalid_nonce"};let issuedAt=timestampMs(r.issued_at),expiresAt=timestampMs(r.expires_at);if(issuedAt===null||expiresAt===null)return{ok:!1,reason:"recovery_invalid_timestamp"};let window=expiresAt-issuedAt;return window<=0?{ok:!1,reason:"recovery_invalid_timestamp"}:window>MAX_ACCEPTANCE_TTL_MS?{ok:!1,reason:"recovery_window_too_long"}:nowMs<issuedAt-MAX_CLOCK_SKEW_MS?{ok:!1,reason:"recovery_not_yet_valid"}:nowMs>expiresAt?{ok:!1,reason:"recovery_expired"}:{ok:!0,env:{schema:TASK_RECOVERY_SCHEMA,grant_hash:r.grant_hash,requester_did:r.requester_did,action_nonce:r.action_nonce,issued_at:r.issued_at,expires_at:r.expires_at}}}async function buildRecoveryRequest(input){let request={schema:TASK_RECOVERY_SCHEMA,grant_hash:input.grantHash,requester_did:input.requesterDid,action_nonce:input.actionNonce,issued_at:new Date(input.nowMs).toISOString(),expires_at:new Date(input.nowMs+input.ttlMs).toISOString()},check=validateRecoveryRequest(request,input.nowMs);if(!check.ok)return{ok:!1,reason:check.reason};let signature=await signCanonical(check.env,input.sign);return signature===null?{ok:!1,reason:"invalid_recovery_signature"}:{ok:!0,request:check.env,signature_base64:signature}}import nacl4 from"tweetnacl";import __naclUtil8 from"tweetnacl-util";const{decodeBase64: decodeBase647}=__naclUtil8;var MINTED=new WeakMap;function containerCopy(value){if(value===null||typeof value!="object")return null;if(Array.isArray(value)){let source=value,sink=[];for(let key of Object.keys(source))sink[key]=source[key];return sink}return{...value}}function rebuildDocumentDeep(document){let top=containerCopy(document);if(top===null)return document;let window=containerCopy(top.grant_ttl_ms);window!==null&&(top.grant_ttl_ms=window);let rawNotes=top.notes;if(Array.isArray(rawNotes)){let count=rawNotes.length,notes=[];for(let i=0;i<count;i+=1){let note=rawNotes[i],frame=containerCopy(note);notes.push(frame===null?note:frame)}top.notes=notes}let rawServices=top.services;if(Array.isArray(rawServices)){let count=rawServices.length,services=[];for(let i=0;i<count;i+=1){let entry=rawServices[i],offering=containerCopy(entry);if(offering===null){services.push(entry);continue}let price=containerCopy(offering.price);price!==null&&(offering.price=price),services.push(offering)}top.services=services}return top}function freezeManifestDeep(m){if(m===null||typeof m!="object")return m;Object.freeze(m.grant_ttl_ms),Object.freeze(m.notes);let services=m.services;if(Array.isArray(services))for(let offering of services)offering!==null&&typeof offering=="object"&&Object.freeze(offering.price),Object.freeze(offering);return Object.freeze(services),Object.freeze(m)}function mintVerifiedProvider(manifest){let document=freezeManifestDeep(rebuildDocumentDeep(manifest)),provider=Object.freeze({manifest:document});return MINTED.set(provider,document),provider}function isVerifiedProvider(value){if(value===null||typeof value!="object"||!MINTED.has(value))return!1;let minted=MINTED.get(value);if(value.manifest!==minted)return!1;let m=minted;if(m===null||typeof m!="object"||typeof m.provider_did!="string"||typeof m.signing_public_key_base64!="string"||typeof m.encryption_public_key_base64!="string")return!1;let ttl=m.grant_ttl_ms;if(ttl===null||typeof ttl!="object"||typeof ttl.min!="number"||typeof ttl.max!="number"||!Array.isArray(m.services))return!1;for(let offering of m.services){if(offering===null||typeof offering!="object")return!1;let o=offering;if(typeof o.ref!="string")return!1;let p=o.price;if(p===null||typeof p!="object"||typeof p.chain!="string"||typeof p.asset!="string"||typeof p.payee_account!="string"||typeof p.min_amount!="string"||typeof p.max_amount!="string")return!1}return!0}var PROVIDER_MANIFEST_SCHEMA="voidly.session.provider.manifest/v1",PROVIDER_MANIFEST_KEYS=["schema","provider_did","signing_public_key_base64","encryption_public_key_base64","attestor_public_key_base64","accept_url","hire_message_schema","worker_base_url","grant_ttl_ms","acceptance_ttl_ms","services","payment_buys","notes","signature_base64"];function manifestSigningBytes(m){return canonicalBytes(m)}function isNonEmptyString(v,max=2048){return typeof v=="string"&&v.length>0&&v.length<=max}function isPositiveInt(v){return typeof v=="number"&&Number.isInteger(v)&&v>0}function verifyManifest(raw,expectedProviderDid){if(typeof raw!="object"||raw===null||Array.isArray(raw))return{ok:!1,reason:"manifest_not_object"};let r={...raw};if(r.schema!==PROVIDER_MANIFEST_SCHEMA)return{ok:!1,reason:"manifest_schema_mismatch"};if(!hasOnlyKeys(r,PROVIDER_MANIFEST_KEYS))return{ok:!1,reason:"manifest_unexpected_field"};if(r.signature_base64===void 0||r.signature_base64===null)return{ok:!1,reason:"manifest_signature_missing"};if(typeof r.signature_base64!="string"||r.signature_base64.length===0)return{ok:!1,reason:"manifest_signature_malformed"};let signature;try{signature=decodeBase647(r.signature_base64)}catch{return{ok:!1,reason:"manifest_signature_malformed"}}if(signature.length!==nacl4.sign.signatureLength)return{ok:!1,reason:"manifest_signature_malformed"};if(!isBase64Key32(r.signing_public_key_base64,decodeBase647))return{ok:!1,reason:"manifest_field_malformed"};if(!isBase64Key32(r.encryption_public_key_base64,decodeBase647))return{ok:!1,reason:"manifest_field_malformed"};if(!isBase64Key32(r.attestor_public_key_base64,decodeBase647))return{ok:!1,reason:"manifest_field_malformed"};if(typeof r.provider_did!="string"||!DID_RE.test(r.provider_did))return{ok:!1,reason:"manifest_field_malformed"};if(!isNonEmptyString(r.accept_url)||!isNonEmptyString(r.worker_base_url))return{ok:!1,reason:"manifest_field_malformed"};if(r.hire_message_schema!==SESSION_HIRE_SCHEMA)return{ok:!1,reason:"manifest_field_malformed"};if(r.payment_buys!=="an attempt, not an outcome")return{ok:!1,reason:"manifest_field_malformed"};if(!isPositiveInt(r.acceptance_ttl_ms))return{ok:!1,reason:"manifest_field_malformed"};let ttl=r.grant_ttl_ms;if(typeof ttl!="object"||ttl===null||Array.isArray(ttl))return{ok:!1,reason:"manifest_field_malformed"};let ttlSnap={...ttl};if(!hasOnlyKeys(ttlSnap,["min","max"]))return{ok:!1,reason:"manifest_field_malformed"};if(!isPositiveInt(ttlSnap.min)||!isPositiveInt(ttlSnap.max))return{ok:!1,reason:"manifest_field_malformed"};if(ttlSnap.min>ttlSnap.max||ttlSnap.max>MAX_GRANT_TTL_MS)return{ok:!1,reason:"manifest_field_malformed"};let rawNotes=r.notes;if(!Array.isArray(rawNotes))return{ok:!1,reason:"manifest_field_malformed"};let notes=[];for(let i=0;i<rawNotes.length;i+=1){let note=rawNotes[i];if(!isNonEmptyString(note,4096))return{ok:!1,reason:"manifest_field_malformed"};notes.push(note)}let rawServices=r.services;if(!Array.isArray(rawServices)||rawServices.length===0)return{ok:!1,reason:"manifest_field_malformed"};let services=[];for(let i=0;i<rawServices.length;i+=1){let entry=rawServices[i];if(typeof entry!="object"||entry===null||Array.isArray(entry))return{ok:!1,reason:"manifest_field_malformed"};let s={...entry};if(!hasOnlyKeys(s,["ref","description","price"]))return{ok:!1,reason:"manifest_field_malformed"};if(!isNonEmptyString(s.ref,MAX_SERVICE_REF_LENGTH)||typeof s.description!="string")return{ok:!1,reason:"manifest_field_malformed"};let price=s.price;if(typeof price!="object"||price===null||Array.isArray(price))return{ok:!1,reason:"manifest_field_malformed"};let p={...price};if(!hasOnlyKeys(p,["chain","asset","payee_account","min_amount","max_amount"]))return{ok:!1,reason:"manifest_field_malformed"};if(typeof p.chain!="string"||!isCaip2(p.chain))return{ok:!1,reason:"manifest_field_malformed"};if(typeof p.asset!="string"||!isCaip19(p.asset))return{ok:!1,reason:"manifest_field_malformed"};if(typeof p.payee_account!="string"||!isCaip10(p.payee_account))return{ok:!1,reason:"manifest_field_malformed"};if(typeof p.min_amount!="string"||!isPositiveDecimalString(p.min_amount))return{ok:!1,reason:"manifest_field_malformed"};if(typeof p.max_amount!="string"||!isPositiveDecimalString(p.max_amount))return{ok:!1,reason:"manifest_field_malformed"};services.push({ref:s.ref,description:s.description,price:{chain:p.chain,asset:p.asset,payee_account:p.payee_account,min_amount:p.min_amount,max_amount:p.max_amount}})}let signingKey=decodeBase647(r.signing_public_key_base64),derived=null;try{derived=deriveDidFromSigningKey(signingKey)}catch{derived=null}if(derived===null||derived!==r.provider_did)return{ok:!1,reason:"manifest_did_not_derived"};let body={schema:PROVIDER_MANIFEST_SCHEMA,provider_did:r.provider_did,signing_public_key_base64:r.signing_public_key_base64,encryption_public_key_base64:r.encryption_public_key_base64,attestor_public_key_base64:r.attestor_public_key_base64,accept_url:r.accept_url,hire_message_schema:SESSION_HIRE_SCHEMA,worker_base_url:r.worker_base_url,grant_ttl_ms:{min:ttlSnap.min,max:ttlSnap.max},acceptance_ttl_ms:r.acceptance_ttl_ms,services,payment_buys:"an attempt, not an outcome",notes},verified=!1;try{verified=nacl4.sign.detached.verify(manifestSigningBytes(body),signature,signingKey)}catch{verified=!1}return verified?expectedProviderDid!==void 0&&expectedProviderDid!==body.provider_did?{ok:!1,reason:"manifest_did_not_pinned"}:{ok:!0,manifest:{...body,signature_base64:r.signature_base64}}:{ok:!1,reason:"manifest_signature_invalid"}}function verifyProvider(raw,expectedProviderDid){if(typeof expectedProviderDid!="string"||!DID_RE.test(expectedProviderDid))return{ok:!1,reason:"manifest_pin_not_a_did"};let verdict=verifyManifest(raw,expectedProviderDid);if(!verdict.ok)return{ok:!1,reason:verdict.reason};let refs=new Set;for(let offering of verdict.manifest.services){if(refs.has(offering.ref))return{ok:!1,reason:"manifest_service_ref_duplicated"};refs.add(offering.ref)}return{ok:!0,provider:mintVerifiedProvider(verdict.manifest)}}function isSessionParty(value){if(value===null||typeof value!="object")return!1;let p=value;return typeof p.did!="string"||p.did.length===0?!1:p.signingPublicKey instanceof Uint8Array&&p.signingPublicKey.length===32}import __naclUtil9 from"tweetnacl-util";const{decodeBase64: decodeBase648, encodeBase64: encodeBase644}=__naclUtil9;function copyFixedBytes(value,expectedLength){if(!ArrayBuffer.isView(value)||!(value instanceof Uint8Array))return null;let copy=new Uint8Array(value);return copy.length===expectedLength?copy:null}function decodeKey32(base64){if(typeof base64!="string")return null;try{let k=decodeBase648(base64);return k.length===32?k:null}catch{return null}}async function privateHire(input){let hirer={...input.hirer},provider=input.provider,service={...input.service},task={...input.task},price={...input.price},ttl={...input.ttl},entropy={...input.entropy},nowMs=input.nowMs;if(!isPlausibleNowMs(nowMs))return{ok:!1,reason:"clock_implausible"};if(typeof task.brief!="string"||task.brief.length>MAX_BRIEF_LENGTH)return{ok:!1,reason:"brief_too_long"};if(!Number.isInteger(ttl.offerMs)||!Number.isInteger(ttl.grantMs)||ttl.offerMs<=0||ttl.grantMs<=0||ttl.offerMs>MAX_OFFER_TTL_MS||ttl.grantMs>MAX_GRANT_TTL_MS||ttl.grantMs>ttl.offerMs)return{ok:!1,reason:"invalid_ttl"};if(ttl.grantMs<MIN_GRANT_TTL_MS)return{ok:!1,reason:"grant_ttl_below_settlement_depth"};let sessionKey=copyFixedBytes(entropy.sessionKey,32);if(sessionKey===null)return{ok:!1,reason:"invalid_session_key_length"};if(isAllZero(sessionKey))return{ok:!1,reason:"invalid_session_key_all_zero"};let ephemeralSecretKey=copyFixedBytes(entropy.ephemeralSecretKey,32);if(ephemeralSecretKey===null)return{ok:!1,reason:"invalid_ephemeral_secret_length"};if(isAllZero(ephemeralSecretKey))return{ok:!1,reason:"invalid_ephemeral_secret_all_zero"};let briefSalt=copyFixedBytes(entropy.briefSalt,32);if(briefSalt===null)return{ok:!1,reason:"invalid_brief_salt_length"};if(isAllZero(briefSalt))return{ok:!1,reason:"invalid_brief_salt_all_zero"};let bodyNonce=copyFixedBytes(entropy.bodyNonce,24);if(bodyNonce===null)return{ok:!1,reason:"invalid_body_nonce_length"};if(isAllZero(bodyNonce))return{ok:!1,reason:"invalid_body_nonce_all_zero"};let wrapNonce=copyFixedBytes(entropy.wrapNonce,24);if(wrapNonce===null)return{ok:!1,reason:"invalid_wrap_nonce_length"};if(isAllZero(wrapNonce))return{ok:!1,reason:"invalid_wrap_nonce_all_zero"};if(!isVerifiedProvider(provider))return{ok:!1,reason:"provider_not_verified"};let manifest=provider.manifest,providerDid=manifest.provider_did,providerSigningKeyBase64=manifest.signing_public_key_base64,providerBinding=providerIdentityBindingFailure(providerDid,providerSigningKeyBase64);if(providerBinding!==null)return{ok:!1,reason:providerBinding};let providerEncKey=decodeKey32(manifest.encryption_public_key_base64);if(!providerEncKey)return{ok:!1,reason:"invalid_recipient_enc_pubkey"};let offering=manifest.services.find(s=>s.ref===service.ref);if(offering===void 0)return{ok:!1,reason:"provider_service_not_offered"};if(price.chain!==offering.price.chain)return{ok:!1,reason:"provider_price_chain_not_offered"};if(price.asset!==offering.price.asset)return{ok:!1,reason:"provider_price_asset_not_offered"};if(price.payeeAccount!==offering.price.payee_account)return{ok:!1,reason:"provider_payee_not_manifested"};let floorCmp=compareDecimalStrings(price.minAmount,offering.price.min_amount);if(floorCmp===null)return{ok:!1,reason:"offer_invalid_price_min_amount"};if(floorCmp<0)return{ok:!1,reason:"provider_price_below_manifest_floor"};let ceilingCmp=compareDecimalStrings(price.maxAmount,offering.price.max_amount);if(ceilingCmp===null)return{ok:!1,reason:"offer_invalid_price_amount"};if(ceilingCmp>0)return{ok:!1,reason:"provider_price_above_manifest_ceiling"};if(ttl.grantMs<manifest.grant_ttl_ms.min)return{ok:!1,reason:"provider_grant_ttl_below_manifest_floor"};if(ttl.grantMs>manifest.grant_ttl_ms.max)return{ok:!1,reason:"provider_grant_ttl_above_manifest_ceiling"};if(x402SessionAccountSpellingIsUnpayable(price.payerAccount))return{ok:!1,reason:"grant_payer_account_not_canonical"};if(x402SessionAccountSpellingIsUnpayable(price.payeeAccount))return{ok:!1,reason:"grant_payee_account_not_canonical"};let offer={schema:SESSION_OFFER_SCHEMA,hirer_did:hirer.did,hirer_signing_pubkey_base64:hirer.signingPublicKeyBase64,provider_did:providerDid,service_ref:service.ref,price_chain:price.chain,price_asset:price.asset,price_payer_account:price.payerAccount,price_payee_account:price.payeeAccount,price_min_amount:price.minAmount,price_max_amount:price.maxAmount,nonce:entropy.offerNonce,issued_at:new Date(nowMs).toISOString(),expires_at:new Date(nowMs+ttl.offerMs).toISOString()},offerCheck=validateOffer(offer,nowMs);if(!offerCheck.ok)return{ok:!1,reason:offerCheck.reason};let offerSignature=await signCanonical(offerCheck.env,hirer.sign);if(offerSignature===null)return{ok:!1,reason:"signer_failed"};let offerHash=await envelopeHash(offerCheck.env);if(!sealedPayloadFitsTransport(briefPayloadBytes(task.brief,briefSalt)))return{ok:!1,reason:"brief_too_long"};let sealed;try{sealed=await sealCapsule({brief:task.brief,offerHash,recipientEncPublicKey:providerEncKey,sessionKeyBytes:sessionKey,ephemeralSecretKey,briefSalt,bodyNonce,wrapNonce})}catch{return{ok:!1,reason:"capsule_frame_failed"}}let capsuleHash=await envelopeHash(sealed.capsule),grant={schema:TASK_GRANT_SCHEMA,hirer_did:hirer.did,provider_did:providerDid,provider_signing_pubkey_base64:providerSigningKeyBase64,provider_enc_pubkey_base64:encodeBase644(providerEncKey),offer_hash:offerHash,capsule_hash:capsuleHash,brief_commitment:sealed.briefCommitment,price_chain:price.chain,price_asset:price.asset,price_payer_account:price.payerAccount,price_payee_account:price.payeeAccount,price_min_amount:price.minAmount,price_max_amount:price.maxAmount,nonce:entropy.grantNonce,issued_at:new Date(nowMs).toISOString(),expires_at:new Date(nowMs+ttl.grantMs).toISOString()},grantCheck=validateGrant(grant,nowMs);if(!grantCheck.ok)return{ok:!1,reason:grantCheck.reason};let grantSignature=await signCanonical(grantCheck.env,hirer.sign);if(grantSignature===null)return{ok:!1,reason:"signer_failed"};let grantHash=await envelopeHash(grantCheck.env);return{ok:!0,wire:{offer:offerCheck.env,offer_signature_base64:offerSignature,grant:grantCheck.env,grant_signature_base64:grantSignature,capsule:sealed.capsule},keep:{sessionKey:sealed.sessionKey,grant_hash:grantHash,offer_hash:offerHash,brief_commitment:sealed.briefCommitment}}}var SESSION_PROVIDER_PROOF_HEADER="x-voidly-session-provider-proof",SESSION_PROVIDER_PROOF_SCHEMA="voidly-session-provider-redemption/v1";var SESSION_PROVIDER_PROOF_MAX_WINDOW_MS=12e4;function proofEnvelope(schema,input){let ttl=Math.min(input.ttlMs??12e4,12e4);return{schema,provider_did:input.providerDid,grant_hash:input.grantHash,action_nonce:input.actionNonce,issued_at:new Date(input.nowMs).toISOString(),expires_at:new Date(input.nowMs+ttl).toISOString()}}function sessionProviderProofEnvelope(input){return proofEnvelope(SESSION_PROVIDER_PROOF_SCHEMA,input)}function encodeSessionProviderProof(envelope,signatureBase64){let json=JSON.stringify({envelope,signature:signatureBase64}),bytes=new TextEncoder().encode(json),binary="";for(let b of bytes)binary+=String.fromCharCode(b);return btoa(binary)}var ADDRESS_RE2=/^0x[0-9a-fA-F]{40}$/,EIP155_CHAIN_RE=/^eip155:[1-9][0-9]{0,31}$/,PREFLIGHT_DEFAULT_CHAIN="eip155:8453",MAX_SUPPORTED_RESPONSE_BYTES=256*1024,KNOWN_ASSET_TRANSFER_METHODS=Object.freeze(["eip3009","permit2","erc7710"]),REQUIRED_ASSET_TRANSFER_METHOD="eip3009",X402_V1_NETWORK_ALIASES=Object.freeze(new Map([["base","eip155:8453"],["base-sepolia","eip155:84532"]])),ASSET_ADVERTISEMENT_KEYS=Object.freeze(["asset","assetAddress","asset_address","token","tokenAddress","token_address","usdc","contract","contractAddress"]),FACILITATOR_PREFLIGHT_SCHEMA="voidly.pay.facilitator-preflight/v1",ALWAYS_UNDETERMINED=Object.freeze([Object.freeze({code:"batching_not_advertised",detail:"Whether this facilitator settles several authorizations in one transaction is not a field in the x402 v2 SupportedResponse schema and is not advertised by any facilitator surveyed. A batched settlement produces evidence the session adapter answers `indeterminate` on, because it requires exactly one EIP-3009 authorization per transaction. This preflight cannot rule that out."}),Object.freeze({code:"nonce_control_is_client_side",detail:"The binding nonce is chosen by the payer's client, not the facilitator. @x402/evm@2.23.0's createNonce() returns 32 random bytes with no override, so a stock client cannot produce the required nonce regardless of which facilitator is used. No verdict here addresses that."}),Object.freeze({code:"per_transaction_shape_is_on_chain",detail:"Whether the settling transaction reverts, carries an offsetting outbound transfer, or pays the wrong account is decided on chain and read by the adapter after the fact. Nothing in /supported speaks to it."})]),NOT_ADVERTISED_UNDETERMINED=Object.freeze({code:"transfer_method_not_advertised",detail:"This facilitator does not state an assetTransferMethod for the exact scheme on this chain. The x402 v2 exact-EVM scheme defaults an ABSENT payload field to eip3009 when the token supports it, and Base USDC does \u2014 but that rule is about the payment payload, not about /supported, and the v2 schema marks `extra` optional without saying what its absence means. Not established either way."});function supportedUrlFor(baseUrl){if(typeof baseUrl!="string"||baseUrl.length===0||baseUrl.length>2048)return null;let parsed;try{parsed=new URL(baseUrl)}catch{return null}if(parsed.protocol!=="https:")return null;parsed.search="",parsed.hash="";let path=parsed.pathname.replace(/\/+$/,"");return parsed.pathname=path.endsWith("/supported")?path:`${path}/supported`,parsed.toString()}function isPlainObject(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function parseSupportedKinds(body){if(!isPlainObject(body))return null;let raw=body.kinds;if(!Array.isArray(raw))return null;let kinds=[];for(let entry of raw){if(!isPlainObject(entry))continue;let{x402Version,scheme,network,extra}=entry;typeof scheme!="string"||scheme.length===0||typeof network!="string"||network.length===0||typeof x402Version!="number"||!Number.isFinite(x402Version)||kinds.push({x402Version,scheme,network,extra:isPlainObject(extra)?extra:null})}return kinds}function resolveKindChain(kind){return EIP155_CHAIN_RE.test(kind.network)?kind.network:kind.x402Version===1?X402_V1_NETWORK_ALIASES.get(kind.network)??null:null}function readTransferMethod(extra){if(!extra||!("assetTransferMethod"in extra))return null;let value=extra.assetTransferMethod;return typeof value!="string"?"":value.trim().toLowerCase()}function readAdvertisedAsset(extra){if(!extra)return null;for(let key of ASSET_ADVERTISEMENT_KEYS){if(!(key in extra))continue;let value=extra[key];if(typeof value!="string")return"";let trimmed=value.trim();if(ADDRESS_RE2.test(trimmed))return trimmed.toLowerCase();let tail=trimmed.split(":").pop()??"";return ADDRESS_RE2.test(tail)?tail.toLowerCase():""}return null}function result(ctx,verdict,reason,detail,matched,extraUndetermined=[]){return Object.freeze({schema:FACILITATOR_PREFLIGHT_SCHEMA,verdict,reason,detail,chain:ctx.chain,frozenAsset:ctx.frozenAsset,url:ctx.url,httpStatus:ctx.httpStatus,matched,observations:Object.freeze([...ctx.observations??[]]),undetermined:Object.freeze([...extraUndetermined,...ALWAYS_UNDETERMINED])})}function decideFromSupported(args){let{body,chain,url,httpStatus}=args,frozenAsset=X402_SESSION_USDC_BY_CHAIN.get(chain)??null,base={chain,url,httpStatus,frozenAsset};if(frozenAsset===null)return result(base,"unknown","chain_not_supported_by_rail",`The session adapter has no frozen USDC address for ${chain}, so there is nothing to check a facilitator against. Nothing about this facilitator was established.`,null);let kinds=parseSupportedKinds(body);if(kinds===null)return result(base,"unknown","response_malformed","The response parsed as JSON but carries no readable `kinds` array, so it is not an x402 SupportedResponse. Nothing about this facilitator was established.",null);let observations=[];isPlainObject(body)&&(!Array.isArray(body.extensions)||!isPlainObject(body.signers))&&observations.push({code:"supported_response_not_v2_conformant",detail:"`extensions` and `signers` are both REQUIRED by the x402 v2 SupportedResponse schema and at least one is missing or the wrong type. Not a disqualifier \u2014 a v1-only facilitator has no reason to publish them \u2014 but it means this document is not a conformant v2 response."});let onChain=kinds.map(k=>({kind:k,resolved:resolveKindChain(k)})).filter(k=>k.resolved===chain);if(onChain.length===0)return result({...base,observations},"unusable","chain_not_offered",`This facilitator lists no kind on ${chain} under any scheme or protocol version, so it cannot settle a payment there.`,null);let otherSchemes=[...new Set(onChain.map(k=>k.kind.scheme))].filter(s=>s!=="exact");otherSchemes.length>0&&observations.push({code:"other_schemes_on_chain",detail:`Also advertises on ${chain}: ${otherSchemes.join(", ")}. This rail uses \`exact\` only.`});let batchSchemes=otherSchemes.filter(s=>/batch/i.test(s));batchSchemes.length>0&&observations.push({code:"batch_scheme_advertised",detail:`Advertises ${batchSchemes.join(", ")} on ${chain}, so this facilitator CAN batch settlements. That is not proof it batches the \`exact\` scheme \u2014 and the absence of such a scheme would not have been proof that it does not. Batching remains undetermined either way.`});let exacts=onChain.filter(k=>k.kind.scheme==="exact");if(exacts.length===0)return result({...base,observations},"unusable","no_exact_on_chain",`This facilitator is present on ${chain} but offers no \`exact\` scheme there${otherSchemes.length?` (only: ${otherSchemes.join(", ")})`:""}. The session rail settles \`exact\` and nothing else.`,null);let disqualifying=k=>{let m=readTransferMethod(k.kind.extra);return!m||m===REQUIRED_ASSET_TRANSFER_METHOD?1:KNOWN_ASSET_TRANSFER_METHODS.includes(m)?0:1},kind=[...exacts].sort((a,b)=>{let stated=k=>readTransferMethod(k.kind.extra)?0:1,caip=k=>EIP155_CHAIN_RE.test(k.kind.network)?0:1;return disqualifying(a)-disqualifying(b)||caip(a)-caip(b)||stated(a)-stated(b)||b.kind.x402Version-a.kind.x402Version})[0].kind,method=readTransferMethod(kind.extra),matched=Object.freeze({x402Version:kind.x402Version,scheme:kind.scheme,network:kind.network,resolvedChain:chain,assetTransferMethod:method===null||method===""?null:method});EIP155_CHAIN_RE.test(kind.network)||observations.push({code:"matched_via_v1_alias",detail:`Matched the x402 v1 network alias \`${kind.network}\` to ${chain}. The facilitator publishes no CAIP-2 entry for this chain.`}),kind.x402Version===1&&observations.push({code:"v1_exact_is_eip3009_by_spec",detail:"The matched entry is x402Version 1, whose specification states plainly that the `exact` scheme uses EIP-3009 transferWithAuthorization \u2014 v1 has no permit2 option. That is stronger evidence than a v2 entry with no `extra`, but it is evidence about the protocol version rather than about this facilitator's implementation, and the v1 payload shape is one this rail has not exercised end to end. It does not by itself earn a `usable` verdict."});let minimum=kind.extra?.minPaymentAmountAtomic;if(typeof minimum=="string"&&/^[0-9]+$/.test(minimum)&&minimum!=="0"&&observations.push({code:"minimum_payment_advertised",detail:`Refuses payments below ${minimum} atomic units on ${chain}. A grant priced under that cannot be paid through this facilitator.`}),method==="permit2")return result({...base,observations},"unusable","transfer_method_permit2",`This facilitator settles \`exact\` on ${chain} via Permit2. A Permit2 settlement emits no AuthorizationUsed log and carries no bytes32 EIP-3009 nonce, so the grant binding has nowhere to live and the session adapter will answer \`indeterminate\` on a payment that really happened. Do not pay through this facilitator.`,matched);if(method!==null&&method!==""&&method!==REQUIRED_ASSET_TRANSFER_METHOD&&KNOWN_ASSET_TRANSFER_METHODS.includes(method))return result({...base,observations},"unusable","transfer_method_not_eip3009",`This facilitator settles \`exact\` on ${chain} via \`${method}\`, which is not an EIP-3009 authorization path. It emits no AuthorizationUsed log for the session adapter to read, so a payment settled here can never verify.`,matched);let advertisedAsset=readAdvertisedAsset(kind.extra);return advertisedAsset===""?result({...base,observations},"unknown","asset_advertisement_unreadable","This facilitator pins a token on the matched entry but the value is not a readable address, so it could not be compared against the frozen USDC contract. Refusing requires establishing a mismatch, and no mismatch was established.",matched,method===null?[NOT_ADVERTISED_UNDETERMINED]:[]):advertisedAsset!==null&&advertisedAsset!==frozenAsset?result({...base,observations},"unusable","asset_not_frozen_usdc",`This facilitator pins ${advertisedAsset} as the token on ${chain}. The session adapter derives ${frozenAsset} from the chain and will not read a transfer of anything else, so a payment settled here can never verify.`,matched):method===null?result({...base,observations},"unknown","transfer_method_not_advertised",`This facilitator offers \`exact\` on ${chain} and nothing in its /supported document disqualifies it \u2014 but it does not state an assetTransferMethod, so whether it settles via EIP-3009 (which this rail can verify) or Permit2 (which it cannot) is not established. Confirm with the facilitator before paying.`,matched,[NOT_ADVERTISED_UNDETERMINED]):method===""?result({...base,observations},"unknown","transfer_method_unrecognised","The matched entry carries an assetTransferMethod that is not a string. Nothing was established about how this facilitator settles.",matched,[NOT_ADVERTISED_UNDETERMINED]):method===REQUIRED_ASSET_TRANSFER_METHOD?result({...base,observations},"usable","eip3009_exact_advertised",`This facilitator advertises \`exact\` on ${chain} settled via EIP-3009, which is the authorization path the session adapter reads. Nothing in its /supported document disqualifies it. That is the whole of what was checked \u2014 see \`undetermined\`, which is never empty: batching, client-side nonce control and the shape of the settling transaction are all outside this document.`,matched):result({...base,observations},"unknown","transfer_method_unrecognised",`This facilitator advertises assetTransferMethod \`${method}\`, which this module does not recognise. It is neither known-good nor known-bad, so nothing was established. Treat as unverified until someone reads the method's specification.`,matched,[NOT_ADVERTISED_UNDETERMINED])}async function preflightFacilitator(input){let chain=input.chain??PREFLIGHT_DEFAULT_CHAIN,frozenAsset=X402_SESSION_USDC_BY_CHAIN.get(chain)??null,url=supportedUrlFor(input.baseUrl);if(url===null)return result({chain,url:String(input.baseUrl??""),httpStatus:null,frozenAsset},"unknown","url_not_https","Refused to fetch: the facilitator URL is not a well-formed https URL. A discovery document read over cleartext can be rewritten in flight, so nothing was requested and nothing about this facilitator was established.",null);let base={chain,url,httpStatus:null,frozenAsset},response;try{response=await input.fetchImpl(url,{method:"GET",headers:{accept:"application/json"},...input.signal?{signal:input.signal}:{}})}catch{return result(base,"unknown","unreachable","The request to /supported did not complete \u2014 a refused connection, a TLS failure, a timeout or a reset. The facilitator may be perfectly fine; we did not reach it.",null)}let httpStatus=typeof response?.status=="number"?response.status:null,withStatus={...base,httpStatus},observations=[],finalUrl=typeof response?.url=="string"?response.url:"";if(finalUrl&&finalUrl!==url&&observations.push({code:"redirected",detail:`The request was aimed at ${url} and ended at ${finalUrl}. Redirects across hosts are normal for facilitators (facilitator.dexter.cash 308s to x402.dexter.cash), but the document that was read is the one served by the final host.`}),httpStatus===null||httpStatus<200||httpStatus>=300)return result({...withStatus,observations},"unknown","http_status",`/supported answered ${httpStatus??"no readable status"}. That says nothing about which schemes the facilitator supports; it says the document was not served.`,null);let text;try{text=await response.text()}catch{return result({...withStatus,observations},"unknown","unreachable","The status arrived and the body did not. Nothing was established.",null)}if(text.length>MAX_SUPPORTED_RESPONSE_BYTES)return result({...withStatus,observations},"unknown","response_too_large",`/supported returned ${text.length} bytes, over the ${MAX_SUPPORTED_RESPONSE_BYTES}-byte cap. It was not parsed. A discovery document this size is not one.`,null);let body;try{body=JSON.parse(text)}catch{return result({...withStatus,observations},"unknown","response_not_json","/supported answered 2xx with something that is not JSON. An HTML error or landing page is the ordinary case: a facilitator whose base URL is not its API root serves the marketing site here, and https://x402.org/supported returns a 50KB HTML 404 while the real endpoint is https://x402.org/facilitator/supported. Nothing was established.",null)}let decided=decideFromSupported({body,chain,url,httpStatus});return observations.length===0?decided:Object.freeze({...decided,observations:Object.freeze([...observations,...decided.observations])})}var HEX64_RE2=/^[0-9a-f]{64}$/,EVM_ADDRESS_RE=/^0x[0-9a-fA-F]{40}$/;async function settlementNonce(grantHash){if(typeof grantHash!="string"||!HEX64_RE2.test(grantHash))throw new RangeError("settlementNonce: grantHash must be 64-char lowercase hex");return`0x${await settlementBindingReference(grantHash)}`}var EVM_USDC_EIP712_DOMAINS=Object.freeze(new Map([["eip155:8453",Object.freeze({name:"USD Coin",version:"2",chainId:8453,verifyingContract:"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",domainSeparator:"0x02fa7265e7c5d81118673727957699e4d68f74cd74b7db77da710fe8a2c7834f"})],["eip155:84532",Object.freeze({name:"USDC",version:"2",chainId:84532,verifyingContract:"0x036cbd53842c5426634e7929541ec2318f3dcf7e",domainSeparator:"0x71f17a3b2ff373b803d70a5a07c046c1a2bc8e89c09ef722fcb047abe94c9818"})]])),TRANSFER_WITH_AUTHORIZATION_TYPEHASH="0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267",RECEIVE_WITH_AUTHORIZATION_TYPEHASH="0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8";function evmAddress(value,chain){if(typeof value!="string")return null;if(EVM_ADDRESS_RE.test(value))return value.toLowerCase();if(isCaip10(value)){let prefix=`${chain}:`;if(!value.startsWith(prefix))return null;let addr=value.slice(prefix.length);return EVM_ADDRESS_RE.test(addr)?addr.toLowerCase():null}return null}var EIP3009_AUTHORIZATION_MEMBERS=Object.freeze([Object.freeze({name:"from",type:"address"}),Object.freeze({name:"to",type:"address"}),Object.freeze({name:"value",type:"uint256"}),Object.freeze({name:"validAfter",type:"uint256"}),Object.freeze({name:"validBefore",type:"uint256"}),Object.freeze({name:"nonce",type:"bytes32"})]),EIP712_DOMAIN_MEMBERS=Object.freeze([Object.freeze({name:"name",type:"string"}),Object.freeze({name:"version",type:"string"}),Object.freeze({name:"chainId",type:"uint256"}),Object.freeze({name:"verifyingContract",type:"address"})]);async function buildAuthorizationParts(input){let domain=EVM_USDC_EIP712_DOMAINS.get(input.chain);if(!domain)return{ok:!1,reason:"unsupported_chain"};if(X402_SESSION_USDC_BY_CHAIN.get(input.chain)!==domain.verifyingContract)return{ok:!1,reason:"unsupported_chain"};let from=evmAddress(input.from,input.chain);if(!from)return{ok:!1,reason:"invalid_from"};let to=evmAddress(input.to,input.chain);if(!to)return{ok:!1,reason:"invalid_to"};if(!isPositiveDecimalString(input.value))return{ok:!1,reason:"invalid_value"};if(!Number.isInteger(input.validAfter)||!Number.isInteger(input.validBefore)||input.validAfter<0||input.validBefore<=input.validAfter)return{ok:!1,reason:"invalid_validity_window"};if(input.validBefore>1e12||input.validAfter>1e12)return{ok:!1,reason:"validity_looks_like_milliseconds"};if(typeof input.grantHash!="string"||!HEX64_RE2.test(input.grantHash))return{ok:!1,reason:"invalid_grant_hash"};let nonce=await settlementNonce(input.grantHash);return{ok:!0,domain:{name:domain.name,version:domain.version,chainId:domain.chainId,verifyingContract:domain.verifyingContract},message:{from,to,value:input.value,validAfter:String(input.validAfter),validBefore:String(input.validBefore),nonce}}}async function buildTransferAuthorizationTypedData(input){let parts=await buildAuthorizationParts(input);return parts.ok?{ok:!0,typedData:{domain:parts.domain,types:{EIP712Domain:EIP712_DOMAIN_MEMBERS,TransferWithAuthorization:EIP3009_AUTHORIZATION_MEMBERS},primaryType:"TransferWithAuthorization",message:parts.message}}:{ok:!1,reason:parts.reason}}async function buildReceiveAuthorizationTypedData(input){let parts=await buildAuthorizationParts(input);return parts.ok?{ok:!0,typedData:{domain:parts.domain,types:{EIP712Domain:EIP712_DOMAIN_MEMBERS,ReceiveWithAuthorization:EIP3009_AUTHORIZATION_MEMBERS},primaryType:"ReceiveWithAuthorization",message:parts.message}}:{ok:!1,reason:parts.reason}}function x402SessionEvidence(transactionHash){if(typeof transactionHash!="string"||!/^0x[0-9a-f]{64}$/.test(transactionHash))throw new RangeError("x402SessionEvidence: transaction hash must be `0x` + 64 LOWERCASE hex. Case matters: canonicalEvidenceId folds hex to lowercase, and one hash with its case flipped was two evidence ids \u2014 one payment buying two tasks.");return{schema:X402_SESSION_EVIDENCE_SCHEMA,transaction_hash:transactionHash}}var SIGNATURE_RE=/^0x[0-9a-fA-F]{130}$/,TX_HASH_RE=/^0x[0-9a-f]{64}$/,ZERO_TX_HASH=`0x${"0".repeat(64)}`,HEX32_RE2=/^0x[0-9a-f]{64}$/,ADDRESS_RE3=/^0x[0-9a-f]{40}$/,UINT256_MAX=(BigInt(1)<<BigInt(256))-BigInt(1);function checkSigningWindow(input){if(typeof input.nowMs!="number"||!Number.isFinite(input.nowMs)||input.nowMs<0)return{ok:!1,reason:"invalid_now_ms",detail:"nowMs must be a finite, non-negative millisecond timestamp."};let nowSeconds=Math.floor(input.nowMs/1e3);return input.validBefore<=nowSeconds?{ok:!1,reason:"authorization_expired",detail:`validBefore is ${input.validBefore} and now is ${nowSeconds} (UNIX seconds). EIP-3009 requires block.timestamp < validBefore, so this authorization would revert with 'FiatTokenV2: authorization is expired' \u2014 costing gas, emitting no AuthorizationUsed log, and leaving the provider a transaction hash that can never carry the settlement binding.`}:input.validAfter>nowSeconds?{ok:!1,reason:"authorization_not_yet_valid",detail:`validAfter is ${input.validAfter} and now is ${nowSeconds} (UNIX seconds). EIP-3009 requires block.timestamp > validAfter. A future-dated authorization has no use on this rail, because the submitter is handed the payload the moment it is signed.`}:null}function splitSignature(raw){if(typeof raw!="string"||!SIGNATURE_RE.test(raw))return{ok:!1,reason:"signature_not_65_bytes",detail:"an EIP-712 signature is exactly 65 bytes spelled `0x` + 130 hex (r \u2016 s \u2016 v). A shorter value is usually a wallet that returned a digest; a longer one is usually two concatenated signatures."};let signature=`0x${raw.slice(2).toLowerCase()}`,r=`0x${signature.slice(2,66)}`,s=`0x${signature.slice(66,130)}`,v=Number.parseInt(signature.slice(130,132),16);return v!==27&&v!==28?{ok:!1,reason:"signature_recovery_id_invalid",detail:`the recovery id is ${v}; USDC passes it to ecrecover, which answers the zero address for anything but 27 or 28. A wallet returning 0/1 has not been normalised here, because adding 27 would silently repair a wallet that may be wrong about something this module cannot see.`}:{ok:!0,signature,r,s,v}}async function signTransferAuthorization(input,signer){let built=await buildTransferAuthorizationTypedData(input);if(!built.ok)return{ok:!1,reason:built.reason,detail:`the authorization was refused before it was signed: ${built.reason}`};let window=checkSigningWindow(input);if(window!==null)return window;let raw;try{raw=await signer(built.typedData)}catch(err){return{ok:!1,reason:"signer_threw",detail:`the injected signer threw: ${err instanceof Error?err.message:String(err)}`}}let split=splitSignature(raw);if(!split.ok)return split;let{signature,r,s,v}=split;return{ok:!0,signed:{typedData:built.typedData,signature,v,r,s,chain:input.chain,grantHash:input.grantHash}}}async function signReceiveAuthorization(input,signer){let built=await buildReceiveAuthorizationTypedData(input);if(!built.ok)return{ok:!1,reason:built.reason,detail:`the authorization was refused before it was signed: ${built.reason}`};let window=checkSigningWindow(input);if(window!==null)return window;let raw;try{raw=await signer(built.typedData)}catch(err){return{ok:!1,reason:"signer_threw",detail:`the injected signer threw: ${err instanceof Error?err.message:String(err)}`}}let split=splitSignature(raw);if(!split.ok)return split;let{signature,r,s,v}=split;return{ok:!0,signed:{typedData:built.typedData,signature,v,r,s,chain:input.chain,grantHash:input.grantHash}}}function buildX402PaymentPayload(signed,matched){let m=signed.typedData.message;return Object.freeze({x402Version:matched.x402Version,scheme:"exact",network:matched.network,payload:Object.freeze({signature:signed.signature,authorization:Object.freeze({from:m.from,to:m.to,value:m.value,validAfter:m.validAfter,validBefore:m.validBefore,nonce:m.nonce})})})}function buildX402PaymentRequirements(signed,matched){let m=signed.typedData.message,domain=EVM_USDC_EIP712_DOMAINS.get(signed.chain),asset=X402_SESSION_USDC_BY_CHAIN.get(signed.chain)??null,amountKey=matched.x402Version>=2?"amount":"maxAmountRequired";return Object.freeze({scheme:"exact",network:matched.network,[amountKey]:m.value,payTo:m.to,asset,maxTimeoutSeconds:Math.max(1,Number(m.validBefore)-Number(m.validAfter)),extra:Object.freeze({name:domain?.name??null,version:domain?.version??null,assetTransferMethod:"eip3009"})})}function settleUrlFor(baseUrl){if(typeof baseUrl!="string"||baseUrl.length===0||baseUrl.length>2048)return null;let parsed;try{parsed=new URL(baseUrl)}catch{return null}if(parsed.protocol!=="https:")return null;parsed.search="",parsed.hash="";let path=parsed.pathname.replace(/\/+$/,"").replace(/\/supported$/,"");return parsed.pathname=`${path}/settle`,parsed.toString()}function preflightAdmitsPayment(preflight,allowUnadvertisedTransferMethod){return preflight.verdict==="usable"?!0:preflight.verdict==="unusable"?!1:allowUnadvertisedTransferMethod&&preflight.reason==="transfer_method_not_advertised"}function createFacilitatorSubmitter(input){let preflight=input.preflight,allow=input.allowUnadvertisedTransferMethod===!0;return Object.freeze({kind:"facilitator",async submit(signed){if(!preflightAdmitsPayment(preflight,allow))return{ok:!1,reason:"facilitator_not_usable",detail:`preflight answered ${preflight.verdict}/${preflight.reason} \u2014 ${preflight.detail}`};if(signed.chain!==preflight.chain)return{ok:!1,reason:"chain_mismatch",detail:`the authorization is for ${signed.chain} and this facilitator was preflighted for ${preflight.chain}. A preflight of one chain says nothing about another.`};let matched=preflight.matched;if(matched===null)return{ok:!1,reason:"facilitator_not_usable",detail:"the preflight matched no kind, so there is no advertised network spelling to address a /settle body with."};let url=settleUrlFor(preflight.url);if(url===null)return{ok:!1,reason:"facilitator_not_usable",detail:`could not derive a /settle URL from ${preflight.url}`};let body={x402Version:matched.x402Version,paymentPayload:buildX402PaymentPayload(signed,matched),paymentRequirements:buildX402PaymentRequirements(signed,matched)},res;try{res=await input.fetchImpl(url,{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify(body),...input.signal?{signal:input.signal}:{}})}catch(err){return{ok:!1,reason:"unreachable",detail:`the /settle call did not complete. THIS IS NOT A REFUSAL: the request may have arrived and settled. Do not re-sign a fresh authorization \u2014 the same one is idempotent on chain, because USDC marks (authorizer, nonce) consumed forever. (${err instanceof Error?err.message:String(err)})`}}let parsed;try{parsed=JSON.parse(await res.text())}catch{return{ok:!1,reason:"facilitator_response_unreadable",detail:`/settle answered ${res.status} with a body that is not JSON.`}}let declared=readDeclaredFailure(parsed),hash=readTransactionHash(parsed);return res.status>=200&&res.status<300?declared.failed?{ok:!1,reason:"facilitator_refused",detail:`/settle answered ${res.status} with success:false`+(declared.reason===null?"":` (${declared.reason})`)+(hash===null?".":` and named ${hash} anyway. The facilitator's own answer says it did not settle; presenting that hash would buy a redemption that can never resolve.`)}:hash===null?{ok:!1,reason:"facilitator_response_unreadable",detail:`/settle answered ${res.status} and no readable transaction hash was in it. A facilitator that reports success without a hash has told us nothing the adapter can verify, which is the same as telling us nothing.`}:{ok:!0,transactionHash:hash}:{ok:!1,reason:"facilitator_refused",detail:`/settle answered ${res.status}`+(declared.reason===null?"":` (${declared.reason})`)+(hash===null?".":` and named ${hash} anyway. A hash beside a non-2xx answer is echoed input or a failed attempt, not a settlement.`)}}})}function readTransactionHash(body){if(typeof body!="object"||body===null)return null;let o=body;for(let key of["transaction","transactionHash","transaction_hash","txHash","tx_hash","hash"]){let v=o[key];if(typeof v!="string")continue;let lower=v.toLowerCase();if(lower!==ZERO_TX_HASH&&TX_HASH_RE.test(lower))return lower}return null}var FAILURE_REASON_KEYS=["errorReason","error_reason","error","reason","message"];function readDeclaredFailure(body){if(typeof body!="object"||body===null)return{failed:!1,reason:null};let o=body,reason=null;for(let key of FAILURE_REASON_KEYS){let v=o[key];if(typeof v=="string"&&v.length>0){reason=v.slice(0,256);break}}return{failed:o.success===!1,reason}}var TRANSFER_WITH_AUTHORIZATION_SELECTOR="0xe3ee160e",RECEIVE_WITH_AUTHORIZATION_SELECTOR="0xef55bec6";function word(hexNo0x){return hexNo0x.padStart(64,"0")}function uintWord(decimal){if(!/^(?:0|[1-9][0-9]{0,77})$/.test(decimal))return null;let n;try{n=BigInt(decimal)}catch{return null}return n<BigInt(0)||n>UINT256_MAX?null:word(n.toString(16))}function encodeEip3009Call(selector,signed){let domain=EVM_USDC_EIP712_DOMAINS.get(signed.chain),token=X402_SESSION_USDC_BY_CHAIN.get(signed.chain);if(!domain||!token||token!==domain.verifyingContract)return{ok:!1,reason:"unsupported_chain",detail:`no frozen USDC deployment for ${signed.chain}`};let m=signed.typedData.message;if(!ADDRESS_RE3.test(m.from)||!ADDRESS_RE3.test(m.to))return{ok:!1,reason:"malformed_authorization",detail:"from/to are not addresses"};if(!HEX32_RE2.test(m.nonce))return{ok:!1,reason:"malformed_authorization",detail:"nonce is not 32 bytes"};let value=uintWord(m.value),validAfter=uintWord(m.validAfter),validBefore=uintWord(m.validBefore);if(value===null||validAfter===null||validBefore===null)return{ok:!1,reason:"malformed_authorization",detail:"value/validAfter/validBefore are not uint256 decimal strings"};if(!HEX32_RE2.test(signed.r)||!HEX32_RE2.test(signed.s))return{ok:!1,reason:"malformed_signature",detail:"r/s are not 32 bytes"};if(signed.v!==27&&signed.v!==28)return{ok:!1,reason:"malformed_signature",detail:`recovery id ${signed.v}`};let data=selector+word(m.from.slice(2))+word(m.to.slice(2))+value+validAfter+validBefore+word(m.nonce.slice(2))+word(signed.v.toString(16))+word(signed.r.slice(2))+word(signed.s.slice(2));return{ok:!0,request:Object.freeze({to:token,data,value:"0x0",chainId:domain.chainId})}}function buildTransferWithAuthorizationCalldata(signed){return encodeEip3009Call(TRANSFER_WITH_AUTHORIZATION_SELECTOR,signed)}function buildReceiveWithAuthorizationCalldata(signed){return encodeEip3009Call(RECEIVE_WITH_AUTHORIZATION_SELECTOR,signed)}function createSelfSubmitter(input){return Object.freeze({kind:"self",async submit(signed){let built=buildTransferWithAuthorizationCalldata(signed);if(!built.ok)return{ok:!1,reason:"broadcast_failed",detail:`${built.reason}: ${built.detail}`};let hash;try{hash=await input.broadcast(built.request)}catch(err){return{ok:!1,reason:"broadcast_failed",detail:`the injected broadcaster threw. THIS IS NOT PROOF NOTHING HAPPENED \u2014 a timeout after the transaction reached a mempool looks identical from here. (${err instanceof Error?err.message:String(err)})`}}return typeof hash!="string"||!TX_HASH_RE.test(hash.toLowerCase())?{ok:!1,reason:"broadcast_failed",detail:"the broadcaster returned something that is not a `0x` + 64 hex transaction hash, so there is nothing to present as evidence."}:{ok:!0,transactionHash:hash.toLowerCase()}}})}var SETTLEMENT_HINT_SCHEMA="voidly.session.settlement-hint/v1";function hashSettlementEvidence(evidence){return sha256Hex(canonicalBytes(evidence??null))}async function buildSettlementHint(input){return{schema:SETTLEMENT_HINT_SCHEMA,grant_hash:input.grantHash,provider_did:input.providerDid,evidence_hash:await hashSettlementEvidence(input.evidence),issued_at:new Date(input.nowMs).toISOString()}}var SessionCryptoUnavailableError=class extends Error{constructor(message){super(message);this.code="session_crypto_unavailable";this.name="SessionCryptoUnavailableError"}},SessionUsageError=class extends Error{constructor(message){super(message);this.code="session_usage_error";this.name="SessionUsageError"}},SessionTransportError=class extends Error{constructor(message,status,body){super(message);this.code="session_transport_error";this.name="SessionTransportError",this.status=status,this.body=body}};var SESSION_DEFAULT_TIMEOUT_MS=12e4,SESSION_PATHS=Object.freeze({redeem:"/v1/pay/session/redeem",deliver:"/v1/pay/session/deliver",recover:"/v1/pay/session/recover",reattest:"/v1/pay/session/reattest"}),PAY_RUNTIME_WITHHELD="PAY_RUNTIME_WITHHELD";function errorCodeOf(parsed){if(typeof parsed!="object"||parsed===null)return null;let err=parsed.error;if(typeof err!="object"||err===null)return null;let code=err.code;return typeof code=="string"?code:null}function resolveTimeoutMs(v){if(v===void 0)return SESSION_DEFAULT_TIMEOUT_MS;if(v===null)return null;if(typeof v!="number"||!Number.isFinite(v)||v<=0)throw new SessionUsageError(`session_endpoint_timeout_invalid: SessionEndpoint.timeoutMs must be a finite number of milliseconds greater than zero, or null to declare that the caller owns the deadline. Received ${typeof v=="number"?v:typeof v}. Note that 0 is NOT the spelling for "no timeout" here \u2014 null is.`);return v}function armDeadline(ms){let controller=new AbortController,timer,state={signal:controller.signal,fired:!1,cancel(){timer!==void 0&&clearTimeout(timer)}};timer=setTimeout(()=>{state.fired=!0,controller.abort()},ms);let handle=timer;return typeof handle.unref=="function"&&handle.unref(),state}function doorConsequence(path){switch(path){case SESSION_PATHS.redeem:return"This door SPENDS THE GRANT'S ONE REDEMPTION USE and burns the single-use provider proof header, so the server may already have written the journal row and issued an attestation you never saw. DO NOT PAY AGAIN and do not mint fresh evidence. Re-present the SAME evidence \u2014 or better, call `postReattest` with the same grant and a FRESHLY MINTED proof header, which is the only door that reissues the attestation (a second redeem answers `replayed` and carries none).";case SESSION_PATHS.deliver:return"This door COMMITS THE RESULT WRITE-ONCE and spends no credential and no payment, so the delivery may already be stored. Re-present the SAME receipt and the SAME capsule \u2014 a repeat of identical bytes is safe, while a different result is reported `contested` and never replaces the first.";case SESSION_PATHS.recover:return"This door READS ONLY: it spends no credential, no payment and no redemption, and it cannot have written anything. Nothing needs recovering \u2014 call it again.";case SESSION_PATHS.reattest:return"This door BURNS THE SINGLE-USE PROVIDER PROOF HEADER and nothing else \u2014 no payment, no redemption, no row. Call it again with a FRESHLY MINTED proof header; the one you just sent may be spent, and reusing it is `409 provider_proof_replayed`.";default:return"This client does not know what that path spends, so assume the widest case: the server may have written whatever that door writes. Re-present the SAME artifacts rather than minting new ones."}}function unfinishedCall(path,cause,deadlineMs){let how=deadlineMs===null?`${path} did not complete.`:`${path} was ABANDONED at this client's own ${deadlineMs}ms deadline (session_call_deadline_exceeded) \u2014 the door accepted the call and did not finish answering.`;return new SessionTransportError(`${how} THIS IS NOT A REFUSAL AND IT IS NOT PROOF NOTHING HAPPENED: a timeout after the request reached the server looks identical from here. ${doorConsequence(path)} (${cause instanceof Error?cause.message:String(cause)})`,0,"")}function requestNeverSent(path,what,cause){return new SessionUsageError(`session_request_not_sent: ${path} was never dialled \u2014 ${what}. NOTHING REACHED THE NETWORK, so nothing was journaled, nothing was spent and there is nothing to recover: this is a defect in the arguments, not an unfinished call. Fix the input and call again. (${cause instanceof Error?cause.message:String(cause)})`)}async function post(ep,path,body,headers){let f=ep.fetch??fetch,deadlineMs=resolveTimeoutMs(ep.timeoutMs),url,payload;try{if(typeof f!="function")throw new TypeError(`typeof SessionEndpoint.fetch is ${typeof f}`);url=`${ep.baseUrl.replace(/\/+$/,"")}${path}`,payload=JSON.stringify(body)}catch(err){throw requestNeverSent(path,"the request could not be built (a non-string baseUrl, a body that does not survive JSON.stringify, or a SessionEndpoint.fetch that is not callable)",err)}let clock=deadlineMs===null?null:armDeadline(deadlineMs),refuseIfAbandoned=at=>{if(clock!==null&&clock.fired)throw unfinishedCall(path,at,deadlineMs)};try{let res;try{res=await f(url,{method:"POST",headers:{"content-type":"application/json",...headers??{}},body:payload,...clock===null?{}:{signal:clock.signal}})}catch(err){throw unfinishedCall(path,err,clock!==null&&clock.fired?deadlineMs:null)}refuseIfAbandoned("the deadline fired before the response was in hand");let text;try{text=await res.text()}catch(err){throw clock!==null&&clock.fired?unfinishedCall(path,err,deadlineMs):new SessionTransportError(`${path} answered ${res.status} and the body could not be read: ${err instanceof Error?err.message:String(err)}`,res.status,"")}refuseIfAbandoned("the deadline fired before the body was fully read");let parsed;try{parsed=JSON.parse(text)}catch{throw new SessionTransportError(`${path} answered ${res.status} with a non-JSON body`,res.status,text.slice(0,512))}if(errorCodeOf(parsed)===PAY_RUNTIME_WITHHELD)throw new SessionTransportError(`${path} answered ${res.status} ${PAY_RUNTIME_WITHHELD} \u2014 this endpoint is switched off at the edge, not at the rail. It is answered before routing and before any handler, so no request reached the rail and nothing was journaled. Retrying cannot change it.`,res.status,text.slice(0,512));if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))throw new SessionTransportError(`${path} answered ${res.status} with JSON that is not an object`,res.status,text.slice(0,512));return{status:res.status,body:parsed}}finally{clock!==null&&clock.cancel()}}async function postRedeem(ep,input){return post(ep,SESSION_PATHS.redeem,{offer:input.wire.offer,offer_signature_base64:input.wire.offer_signature_base64,grant:input.wire.grant,grant_signature_base64:input.wire.grant_signature_base64,capsule:input.wire.capsule,acceptance:input.acceptance,acceptance_signature_base64:input.acceptanceSignatureBase64,evidence:input.evidence},{[input.proofHeader.name]:input.proofHeader.value})}async function postDeliver(ep,input){return post(ep,SESSION_PATHS.deliver,{offer:input.wire.offer,offer_signature_base64:input.wire.offer_signature_base64,grant:input.wire.grant,grant_signature_base64:input.wire.grant_signature_base64,receipt:input.receipt,receipt_signature_base64:input.receiptSignatureBase64,result_capsule:input.resultCapsule})}async function postRecover(ep,input){return post(ep,SESSION_PATHS.recover,{offer:input.wire.offer,offer_signature_base64:input.wire.offer_signature_base64,grant:input.wire.grant,grant_signature_base64:input.wire.grant_signature_base64,request:input.request,request_signature_base64:input.requestSignatureBase64})}async function postReattest(ep,input){return post(ep,SESSION_PATHS.reattest,{grant:input.wire.grant},{[input.proofHeader.name]:input.proofHeader.value})}function assertWebCrypto(){let c=globalThis.crypto;if(!c||typeof c.getRandomValues!="function")throw new SessionCryptoUnavailableError("crypto.getRandomValues is unavailable. In a browser this means the page is on an insecure origin; there is no fallback and there must not be one.");return c}function webCryptoEntropy(){return{random(n){if(!Number.isInteger(n)||n<=0||n>1024)throw new RangeError(`webCryptoEntropy.random: n must be 1..1024, got ${String(n)}`);let out=new Uint8Array(n);return assertWebCrypto().getRandomValues(out),out},nonce(){let bytes=new Uint8Array(16);return assertWebCrypto().getRandomValues(bytes),Array.from(bytes).map(b=>b.toString(16).padStart(2,"0")).join("")}}}async function buildHire(input){let e=input.entropy??webCryptoEntropy();return privateHire({hirer:input.hirer,provider:input.provider,service:input.service,task:input.task,price:input.price,ttl:input.ttl,nowMs:input.nowMs,entropy:{offerNonce:e.nonce(),grantNonce:e.nonce(),sessionKey:e.random(32),ephemeralSecretKey:e.random(32),briefSalt:e.random(32),bodyNonce:e.random(24),wrapNonce:e.random(24)}})}function verifyDeliveryReceipt(input){let grantCheck=validateGrant(input.grant,input.nowMs);if(!grantCheck.ok)return{ok:!1,reason:"delivery_grant_mismatch"};let g=grantCheck.env,check=validateDeliveryReceipt(input.receipt,input.nowMs);if(!check.ok)return{ok:!1,reason:check.reason};let receipt=check.env;if(receipt.grant_hash!==input.grantHash)return{ok:!1,reason:"delivery_grant_mismatch"};if(receipt.offer_hash!==g.offer_hash)return{ok:!1,reason:"delivery_grant_mismatch"};if(receipt.provider_did!==g.provider_did)return{ok:!1,reason:"delivery_grant_mismatch"};let providerKey;try{providerKey=decodeBase649(g.provider_signing_pubkey_base64)}catch{return{ok:!1,reason:"invalid_delivery_signature"}}return providerKey.length!==32?{ok:!1,reason:"invalid_delivery_signature"}:verifyDetached(receipt,input.signatureBase64,providerKey)?{ok:!0,receipt}:{ok:!1,reason:"invalid_delivery_signature"}}async function hashArtifact(artifact){return envelopeHash(artifact)}var GRANT_HASH_RE=/^[0-9a-f]{64}$/,UNSIGNED_PLACEHOLDER_SIGNATURE=`0x${"00".repeat(65)}`;async function checkGrantHashAnchor(grant,grantHash){if(typeof grantHash!="string"||!GRANT_HASH_RE.test(grantHash))return"grant_hash_mismatch";let recomputed;try{recomputed=await envelopeHash(grant)}catch{return"grant_hash_mismatch"}return recomputed===grantHash?null:"grant_hash_mismatch"}async function prepareHirePayment(input){let{grant,grantHash,entryPoint}=input,anchor=await checkGrantHashAnchor(grant,grantHash);if(anchor!==null)return{ok:!1,reason:anchor,detail:"the grant handed in does not hash to the grantHash the payment would be bound to. `grant_hash` IS `envelopeHash(grant)`, so these two cannot legitimately disagree \u2014 one of them is a stale, re-parsed or edited copy. Refusing here is what stops a payment being signed for one set of terms and bound to another."};let validBefore=authorizationValidBeforeFor(grant);if(validBefore===null)return{ok:!1,reason:"authorization_expiry_mismatch",detail:"the grant's `expires_at` is not a timestamp this rail can read."};let candidate={scheme:PAYMENT_AUTHORIZATION_SCHEME,entry_point:entryPoint,chain:grant.price_chain,asset:grant.price_asset,from:grant.price_payer_account,to:grant.price_payee_account,value:grant.price_min_amount,valid_after:"0",valid_before:validBefore,nonce:await settlementBindingReference(grantHash),signature:UNSIGNED_PLACEHOLDER_SIGNATURE},shape=validateAuthorizationShape(candidate);if(!shape.ok)return{ok:!1,reason:shape.reason,detail:`the payment this grant derives is not a well-formed authorization: ${shape.reason}. Nothing was signed.`};let bound=await bindAuthorizationToGrant(shape.env,grant,grantHash);return bound!==null?{ok:!1,reason:bound,detail:`the payment this grant derives does not bind to it: ${bound}. Nothing was signed, so the grant's binding nonce is still free and a corrected hire can still be paid.`}:{ok:!0,prepared:{validBefore}}}function wireAuthorizationFrom(input){let{chain,message:m}=input,from=x402SessionAccountCaip10(chain,m.from),to=x402SessionAccountCaip10(chain,m.to),asset=x402SessionAssetCaip19(chain);return from===null||to===null||asset===null?null:{scheme:PAYMENT_AUTHORIZATION_SCHEME,entry_point:input.entryPoint,chain,asset,from,to,value:m.value,valid_after:m.validAfter,valid_before:m.validBefore,nonce:m.nonce.replace(/^0x/,""),signature:input.signature}}async function selfCheckEmitted(authorization,grant,grantHash){let shape=validateAuthorizationShape(authorization);if(!shape.ok)return{reason:shape.reason,detail:`the builder's own output failed the shape validator: ${shape.reason}`};let bound=await bindAuthorizationToGrant(shape.env,grant,grantHash);return bound!==null?{reason:bound,detail:`the builder's own output failed the six-axis binding: ${bound}`}:null}async function buildReceivePaymentAuthorization(input){let{grant,grantHash,nowMs}=input,pre=await prepareHirePayment({grant,grantHash,entryPoint:"receive_with_authorization"});if(!pre.ok)return{ok:!1,reason:pre.reason,detail:pre.detail};let signed=await signReceiveAuthorization({chain:grant.price_chain,from:grant.price_payer_account,to:grant.price_payee_account,value:grant.price_min_amount,validAfter:0,validBefore:Number(pre.prepared.validBefore),grantHash,nowMs},input.sign);if(!signed.ok)return{ok:!1,reason:signed.reason,detail:signed.detail};let authorization=wireAuthorizationFrom({chain:signed.signed.chain,entryPoint:"receive_with_authorization",message:signed.signed.typedData.message,signature:signed.signed.signature});if(authorization===null)return{ok:!1,reason:"authorization_asset_mismatch",detail:`no CAIP spelling exists for ${signed.signed.chain}`};let failed=await selfCheckEmitted(authorization,grant,grantHash);return failed!==null?{ok:!1,reason:failed.reason,detail:failed.detail}:{ok:!0,authorization,signed:signed.signed,grantHash}}async function buildTransferPaymentAuthorization(input){let{grant,grantHash,nowMs}=input,pre=await prepareHirePayment({grant,grantHash,entryPoint:"transfer_with_authorization"});if(!pre.ok)return{ok:!1,reason:pre.reason,detail:pre.detail};let signed=await signTransferAuthorization({chain:grant.price_chain,from:grant.price_payer_account,to:grant.price_payee_account,value:grant.price_min_amount,validAfter:0,validBefore:Number(pre.prepared.validBefore),grantHash,nowMs},input.sign);if(!signed.ok)return{ok:!1,reason:signed.reason,detail:signed.detail};let authorization=wireAuthorizationFrom({chain:signed.signed.chain,entryPoint:"transfer_with_authorization",message:signed.signed.typedData.message,signature:signed.signed.signature});if(authorization===null)return{ok:!1,reason:"authorization_asset_mismatch",detail:`no CAIP spelling exists for ${signed.signed.chain}`};let failed=await selfCheckEmitted(authorization,grant,grantHash);return failed!==null?{ok:!1,reason:failed.reason,detail:failed.detail}:{ok:!0,authorization,signed:signed.signed,grantHash}}async function signHireAuthorization(input){let{grant,grantHash,authorization}=input,anchor=await checkGrantHashAnchor(grant,grantHash);if(anchor!==null)return{ok:!1,reason:anchor,detail:"the grant handed in does not hash to the grantHash this signature would bind the payment to. Signing anyway would produce an attestation naming a hire these bytes do not belong to, which the provider refuses as `authorization_signature_forged` \u2014 loudly, but only after the payer's wallet was already asked."};let binding=await hireAuthorizationBinding(grantHash,authorization);if(!binding.ok)return{ok:!1,reason:binding.reason,detail:`the payment is not a well-formed authorization: ${binding.reason}. Nothing was signed.`};let bound=await bindAuthorizationToGrant(authorization,grant,grantHash);if(bound!==null)return{ok:!1,reason:bound,detail:`this payment does not bind to this grant: ${bound}. Nothing was signed, so the grant's binding nonce is still free and a corrected hire can still be paid.`};let signature=await signCanonical(binding.env,input.sign);return signature===null?{ok:!1,reason:"signer_failed",detail:"the hirer's Ed25519 signer threw, or answered with something that is not 64 bytes. Nothing is in flight; re-attempting is safe."}:{ok:!0,authorizationSignatureBase64:signature}}async function payForGrant(options){let usingFacilitator=options.facilitator!==void 0,usingSelf=options.broadcast!==void 0;if(usingFacilitator===usingSelf)return{ok:!1,reason:"facilitator_unusable",detail:"supply exactly one of `facilitator` (a third party settles) or `broadcast` (the payer settles). Supplying neither has nowhere to send the payment; supplying both hides which one moved the money.",unsigned:!0,preflight:null};let preflight=null,submitter;if(options.facilitator){preflight=await preflightFacilitator({baseUrl:options.facilitator.baseUrl,chain:options.grant.price_chain,fetchImpl:options.facilitator.fetchImpl,...options.signal?{signal:options.signal}:{}});let allow=options.allowUnadvertisedTransferMethod===!0;if(!preflightAdmitsPayment(preflight,allow))return{ok:!1,reason:preflight.reason==="transfer_method_permit2"?"facilitator_permit2":preflight.verdict==="unusable"?"facilitator_unusable":"facilitator_unknown",detail:preflight.detail,unsigned:!0,preflight};submitter=createFacilitatorSubmitter({preflight,fetchImpl:options.facilitator.fetchImpl,allowUnadvertisedTransferMethod:allow,...options.signal?{signal:options.signal}:{}})}else submitter=createSelfSubmitter({broadcast:options.broadcast});let built=await buildTransferPaymentAuthorization({grant:options.grant,grantHash:options.grantHash,nowMs:options.nowMs,sign:options.signer});if(!built.ok)return{ok:!1,reason:built.reason,detail:built.detail,unsigned:!0,preflight};let submitted=await submitter.submit(built.signed);return submitted.ok?{ok:!0,transactionHash:submitted.transactionHash,authorization:built.authorization,signed:built.signed,preflight}:{ok:!1,reason:submitted.reason,detail:submitted.detail,unsigned:!1,preflight}}async function authenticateHireAcceptance(input){let anchor=await checkGrantHashAnchor(input.grant,input.grantHash);if(anchor!==null)return{ok:!1,reason:anchor};let parsed=validateHireAccepted(input.raw,input.nowMs);if(!parsed.ok)return{ok:!1,reason:parsed.reason};let fault=verifyHireAcceptance({accepted:parsed.env,grant:input.grant,grantHash:input.grantHash});return fault!==null?{ok:!1,reason:fault}:{ok:!0,accepted:parsed.env}}async function submitHire(input){let anchor=await checkGrantHashAnchor(input.wire.grant,input.grantHash);if(anchor!==null)return{kind:"unbuildable",reason:anchor};let message=await buildHireMessage(input.wire,input.authorization,input.sign);return message.ok?postHire({url:input.url,message:message.env,grant:input.wire.grant,grantHash:input.grantHash,nowMs:input.nowMs,fetchImpl:input.fetchImpl,...input.signal?{signal:input.signal}:{}}):{kind:"unbuildable",reason:message.reason}}var MAX_HINT_RESPONSE_BYTES=16*1024,MAX_HINT_REFUSAL_WORD_LENGTH=128,HINT_MEDIA_TYPE="application/json";async function submitSettlementHint(input){let anchor=await checkGrantHashAnchor(input.grant,input.grantHash);if(anchor!==null)return{kind:"unbuildable",reason:anchor};let providerDid=input.grant.provider_did;if(typeof providerDid!="string"||providerDid.length===0)return{kind:"unbuildable",reason:"provider_did_unusable"};let hint;try{hint=await buildSettlementHint({grantHash:input.grantHash,providerDid,evidence:input.evidence,nowMs:input.nowMs})}catch{return{kind:"unbuildable",reason:"evidence_unusable"}}let signature=await signCanonical(hint,input.sign);if(signature===null)return{kind:"unbuildable",reason:"signature_failed"};let payload;try{payload=JSON.stringify({hint,hint_signature_base64:signature,evidence:input.evidence})}catch{return{kind:"unbuildable",reason:"evidence_unusable"}}let response;try{response=await input.fetchImpl(input.url,{method:"POST",headers:{"content-type":HINT_MEDIA_TYPE,accept:HINT_MEDIA_TYPE},body:payload,...input.signal?{signal:input.signal}:{}})}catch{return{kind:"undelivered",detail:"transport_failed"}}let text;try{text=await response.text()}catch{return{kind:"undelivered",detail:"response_body_unreadable"}}if(text.length>MAX_HINT_RESPONSE_BYTES)return{kind:"unrecognized",status:response.status,detail:"response_too_large"};let body;try{body=JSON.parse(text)}catch{return response.status>=500?{kind:"undelivered",detail:"response_not_json"}:{kind:"unrecognized",status:response.status,detail:"response_not_json"}}if(typeof body!="object"||body===null||Array.isArray(body))return{kind:"unrecognized",status:response.status,detail:"response_not_object"};let answer=body;if(response.status===202)return answer.status==="accepted"?{kind:"acknowledged",status:response.status,hint}:{kind:"unrecognized",status:response.status,detail:"response_not_an_acknowledgement"};let reason=answer.error;return typeof reason=="string"&&reason.length>0&&reason.length<=MAX_HINT_REFUSAL_WORD_LENGTH?{kind:"refused",status:response.status,reason}:response.status>=500?{kind:"undelivered",detail:"response_not_a_refusal"}:{kind:"unrecognized",status:response.status,detail:"response_not_a_refusal"}}async function openDeliveredResult(input){let anchor=await checkGrantHashAnchor(input.grant,input.grantHash);if(anchor!==null)return{kind:"unverifiable",reason:anchor};let verified=verifyDeliveryReceipt({receipt:input.receipt,signatureBase64:input.signatureBase64,grant:input.grant,grantHash:input.grantHash,nowMs:input.nowMs});if(!verified.ok)return{kind:"unverifiable",reason:verified.reason};let opened=await openResult(input.resultCapsule,input.sessionKey,input.grantHash,verified.receipt.result_commitment);return opened.kind!=="opened"?{kind:"unopenable",receipt:verified.receipt}:{kind:"opened",result:opened.result,receipt:verified.receipt}}var RECOVERY_REQUEST_TTL_MS=5*6e4,MAX_RECOVERY_WORD_LENGTH=128;function wordOf(v){return typeof v!="string"||v.length===0?"":v.length<=MAX_RECOVERY_WORD_LENGTH?v:v.slice(0,MAX_RECOVERY_WORD_LENGTH)}async function recoverResult(input){let anchor=await checkGrantHashAnchor(input.wire.grant,input.grantHash);if(anchor!==null)return{kind:"unbuildable",reason:anchor};let hirerDid=input.wire.grant.hirer_did;if(typeof hirerDid!="string"||hirerDid.length===0)return{kind:"unbuildable",reason:"hirer_did_unusable"};let e=input.entropy??webCryptoEntropy(),built=await buildRecoveryRequest({grantHash:input.grantHash,requesterDid:hirerDid,actionNonce:e.nonce(),nowMs:input.nowMs,ttlMs:input.ttlMs??RECOVERY_REQUEST_TTL_MS,sign:input.sign});if(!built.ok)return{kind:"unbuildable",reason:built.reason};let answered;try{answered=await postRecover(input.endpoint,{wire:input.wire,request:built.request,requestSignatureBase64:built.signature_base64})}catch(err){if(!(err instanceof SessionTransportError))throw err;return err.status===0?{kind:"undelivered",detail:err.message}:{kind:"unrecognized",status:err.status,detail:err.message}}let outcome=answered.body.outcome;if(typeof outcome!="object"||outcome===null||Array.isArray(outcome))return{kind:"unrecognized",status:answered.status,detail:"response_has_no_outcome"};let o=outcome,state=o.state,stateWord=typeof state=="object"&&state!==null&&!Array.isArray(state)?wordOf(state.kind):"",answer=o.answer;if(typeof answer!="object"||answer===null||Array.isArray(answer)){let ev=o.evidence_id;return{kind:"no_result",status:answered.status,outcome:wordOf(o.kind),state:stateWord,...typeof ev=="string"&&ev.length>0?{evidence_id:ev}:{}}}let a=answer;return typeof a.receipt_signature_base64!="string"?{kind:"unverifiable",reason:"answer_malformed"}:openDeliveredResult({receipt:a.receipt,signatureBase64:a.receipt_signature_base64,resultCapsule:a.result_capsule,grant:input.wire.grant,grantHash:input.grantHash,sessionKey:input.sessionKey,nowMs:input.nowMs})}var PROVIDER_MANIFEST_PATH="/.well-known/voidly-session-provider.json";async function fetchVerifiedProvider(input){let response;try{response=await input.fetchImpl(input.manifestUrl,{method:"GET",headers:{accept:"application/json"},...input.signal===void 0?{}:{signal:input.signal}})}catch(e){return{ok:!1,reason:"manifest_unreachable",detail:`fetch failed: ${e.name}`}}if(!response.ok)return{ok:!1,reason:"manifest_unreachable",detail:`http ${response.status}`};let parsed;try{parsed=await response.json()}catch{return{ok:!1,reason:"manifest_not_json",detail:"body is not JSON"}}let verdict=verifyProvider(parsed,input.expectedProviderDid);return verdict.ok?{ok:!0,provider:verdict.provider}:{ok:!1,reason:verdict.reason,detail:verdict.reason}}import __naclUtil10 from"tweetnacl-util";const{decodeBase64: decodeBase6410, encodeBase64: encodeBase645}=__naclUtil10;async function reviewHire(input){let offerCheck=validateOffer(input.wire?.offer,input.nowMs);if(!offerCheck.ok)return{ok:!1,reason:"offer_unreadable"};let offer=offerCheck.env,grantCheck=validateGrant(input.wire?.grant,input.nowMs);if(!grantCheck.ok)return{ok:!1,reason:"grant_unreadable"};let grant=grantCheck.env,capCheck=validateCapsuleShape(input.wire?.capsule);if(!capCheck.ok)return{ok:!1,reason:"capsule_unreadable"};let capsule=capCheck.env;if(!(input.hirerSigningPublicKey instanceof Uint8Array)||input.hirerSigningPublicKey.length!==32)return{ok:!1,reason:"hirer_key_invalid"};if(deriveDidFromSigningKey(input.hirerSigningPublicKey)!==grant.hirer_did)return{ok:!1,reason:"hirer_key_not_derivable"};if(!verifyDetached(offer,input.wire.offer_signature_base64,input.hirerSigningPublicKey))return{ok:!1,reason:"invalid_offer_signature"};if(!verifyDetached(grant,input.wire.grant_signature_base64,input.hirerSigningPublicKey))return{ok:!1,reason:"invalid_grant_signature"};let offerHash=await envelopeHash(offer),grantHash=await envelopeHash(grant),capsuleHash=await envelopeHash(capsule);if(grant.offer_hash!==offerHash)return{ok:!1,reason:"grant_offer_mismatch"};if(grant.capsule_hash!==capsuleHash)return{ok:!1,reason:"grant_capsule_mismatch"};if(capsule.offer_hash!==grant.offer_hash)return{ok:!1,reason:"capsule_offer_mismatch"};if(capsule.recipient_enc_pubkey_base64!==grant.provider_enc_pubkey_base64)return{ok:!1,reason:"recipient_binding_mismatch"};if(grant.price_chain!==offer.price_chain||grant.price_asset!==offer.price_asset||grant.price_payer_account!==offer.price_payer_account||grant.price_payee_account!==offer.price_payee_account||grant.price_min_amount!==offer.price_min_amount||grant.price_max_amount!==offer.price_max_amount)return{ok:!1,reason:"grant_price_mismatch"};if(grant.provider_did!==offer.provider_did)return{ok:!1,reason:"grant_provider_mismatch"};if(grant.hirer_did!==offer.hirer_did)return{ok:!1,reason:"grant_hirer_mismatch"};let grantExpiresAtMs=timestampMs(grant.expires_at),offerExpiresAtMs=timestampMs(offer.expires_at);return grantExpiresAtMs===null||offerExpiresAtMs===null?{ok:!1,reason:"grant_unreadable"}:grantExpiresAtMs>offerExpiresAtMs?{ok:!1,reason:"grant_outlives_offer"}:grant.provider_did!==input.expectedProviderDid?{ok:!1,reason:"provider_did_mismatch"}:{ok:!0,offer,grant,capsule,grantHash,capsuleHash,offerHash,terms:{serviceRef:offer.service_ref,chain:grant.price_chain,asset:grant.price_asset,payerAccount:grant.price_payer_account,payeeAccount:grant.price_payee_account,minAmount:grant.price_min_amount,maxAmount:grant.price_max_amount,grantExpiresAtMs,offerExpiresAtMs}}}async function acceptHire(input){let e=input.entropy??webCryptoEntropy();return buildAcceptance({grantHash:input.grantHash,redeemerDid:input.providerDid,actionNonce:e.nonce(),nowMs:input.nowMs,ttlMs:input.ttlMs??10*6e4,sign:input.sign})}async function buildRedemptionProofHeader(input){let e=input.entropy??webCryptoEntropy(),envelope=sessionProviderProofEnvelope({providerDid:input.providerDid,grantHash:input.grantHash,actionNonce:e.nonce(),nowMs:input.nowMs}),signature=await signCanonical(envelope,input.sign);if(signature===null)throw new SessionUsageError("buildRedemptionProofHeader: the injected signer failed");return{name:SESSION_PROVIDER_PROOF_HEADER,value:encodeSessionProviderProof(envelope,signature)}}async function openBrief(input){return openCapsuleAsProvider({capsule:input.wire?.capsule,grant:input.wire?.grant,grantSignatureBase64:input.wire?.grant_signature_base64,hirerSigningPublicKey:input.hirerSigningPublicKey,attestation:input.attestation,attestationSignatureBase64:input.attestationSignatureBase64,attestorSigningPublicKey:input.attestorSigningPublicKey,providerDid:input.providerDid,recipientEncSecretKey:input.recipientEncSecretKey,nowMs:input.nowMs})}async function sealTaskResult(input){let e=input.entropy??webCryptoEntropy(),briefBodyNonce;try{briefBodyNonce=decodeBase6410(input.briefCapsule.body_nonce_base64)}catch{throw new SessionUsageError("sealTaskResult: the brief capsule's body nonce is unreadable")}if(briefBodyNonce.length!==24)throw new SessionUsageError("sealTaskResult: the brief capsule's body nonce is not 24 bytes");let bodyNonce=e.random(24);for(let i=0;i<4&&encodeBase645(bodyNonce)===input.briefCapsule.body_nonce_base64;i++)bodyNonce=e.random(24);let sealed=await sealResult({result:input.result,grantHash:input.grantHash,sessionKey:input.sessionKey,resultSalt:e.random(32),bodyNonce,briefBodyNonce});return{capsule:sealed.capsule,resultCommitment:sealed.resultCommitment,resultCapsuleHash:await envelopeHash(sealed.capsule)}}async function signDelivery(input){return buildDeliveryReceipt({grantHash:input.grantHash,offerHash:input.offerHash,providerDid:input.providerDid,resultCapsuleHash:input.resultCapsuleHash,resultCommitment:input.resultCommitment,nowMs:input.nowMs,recoverableUntilMs:input.recoverableUntilMs,sign:input.sign})}var HEX64_RE3=/^[0-9a-f]{64}$/;async function prepareRelayAuthorization(input){if(typeof input!="object"||input===null)throw new TypeError("assembleSignedTransferAuthorization: input must be an object");let grant=input.grant;if(typeof grant!="object"||grant===null)throw new TypeError("assembleSignedTransferAuthorization: grant must be a TaskGrantEnvelope");let shape=validateAuthorizationShape(input.authorization);if(!shape.ok)return{ok:!1,reason:shape.reason,detail:`the authorization is not a well-formed ${PAYMENT_AUTHORIZATION_SCHEME} payment authorization: ${shape.reason}. Nothing was hashed, compared or signed.`};let authorization=shape.env,validAfter=Number(authorization.valid_after);if(!Number.isSafeInteger(validAfter))return{ok:!1,reason:"valid_after_not_safe_integer",detail:`valid_after is "${authorization.valid_after}", which does not survive conversion to a JS number without loss. The EIP-712 builder takes seconds as numbers, and a rounded one would be signed into a window nobody agreed to.`};let grantHash=await envelopeHash(grant);if(input.expectedGrantHash!==void 0&&(typeof input.expectedGrantHash!="string"||!HEX64_RE3.test(input.expectedGrantHash)||input.expectedGrantHash!==grantHash))return{ok:!1,reason:"grant_hash_mismatch",detail:`the caller expected grant hash ${String(input.expectedGrantHash)} and this grant hashes to ${grantHash}. The hash is the reference the payment's binding nonce is checked against, so continuing would compare the money to the wrong grant.`};let bound=await bindAuthorizationToGrant(authorization,grant,grantHash);if(bound!==null)return{ok:!1,reason:bound,detail:`the authorization does not bind to grant ${grantHash}: ${bound}. This is the same comparison the redemption path makes, from the same function \u2014 a relay that disagreed with it would be spending gas on a payment no provider can redeem.`};let frozenAsset=x402SessionAssetCaip19(grant.price_chain);if(frozenAsset===null||frozenAsset!==grant.price_asset)return{ok:!1,reason:"authorization_asset_not_frozen_usdc",detail:`the grant prices this hire in ${grant.price_asset} on ${grant.price_chain}, and the only asset this rail settles there is ${String(frozenAsset)}. A transfer of anything else emits no AuthorizationUsed log the settlement adapter recognises, so the payment would move and the redemption would never resolve.`};let validBeforeString=authorizationValidBeforeFor(grant);return validBeforeString===null?{ok:!1,reason:"authorization_expiry_mismatch",detail:"the grant's expires_at does not parse to a millisecond instant, so there is no validBefore to sign against. Unreachable while step 3 runs first; fail-closed anyway."}:{ok:!0,grantHash,entryPoint:authorizationEntryPoint(authorization),signInput:{chain:grant.price_chain,from:grant.price_payer_account,to:grant.price_payee_account,value:authorization.value,validAfter,validBefore:Number(validBeforeString),grantHash,nowMs:input.nowMs},signature:authorization.signature}}function entryPointMismatch(door,declared){return declared==="unstated"||declared===door?null:{ok:!1,reason:"authorization_entry_point_mismatch",detail:`the wire declares entry_point "${declared}" and this is the ${door} door. The two EIP-712 struct hashes differ, so the signatures are not interchangeable: assembling here would produce a well-formed call that recovers some other address and reverts, costing the relayer gas and emitting no AuthorizationUsed log for the settlement binding to live in. Nothing was signed and nothing was sent. This same authorization assembles at the ${declared} door.`}}async function assembleSignedTransferAuthorization(input){let prepared=await prepareRelayAuthorization(input);if(!prepared.ok)return prepared;let wrongDoor=entryPointMismatch("transfer_with_authorization",prepared.entryPoint);if(wrongDoor!==null)return wrongDoor;let signed=await signTransferAuthorization(prepared.signInput,()=>prepared.signature);return signed.ok?{ok:!0,signed:signed.signed,grantHash:prepared.grantHash}:{ok:!1,reason:signed.reason,detail:signed.detail}}async function assembleSignedReceiveAuthorization(input){let prepared=await prepareRelayAuthorization(input);if(!prepared.ok)return prepared;let wrongDoor=entryPointMismatch("receive_with_authorization",prepared.entryPoint);if(wrongDoor!==null)return wrongDoor;let signed=await signReceiveAuthorization(prepared.signInput,()=>prepared.signature);return signed.ok?{ok:!0,signed:signed.signed,grantHash:prepared.grantHash}:{ok:!1,reason:signed.reason,detail:signed.detail}}var READ_ONLY_RPC_METHODS=Object.freeze(["eth_chainId","eth_call","eth_estimateGas","eth_gasPrice","eth_getBalance","eth_blockNumber","eth_getLogs","eth_getBlockByNumber"]),FORBIDDEN_RPC_METHODS=Object.freeze(["eth_sendRawTransaction","eth_sendTransaction","eth_sign","eth_signTransaction","eth_signTypedData","eth_signTypedData_v4","eth_accounts","personal_sign","personal_unlockAccount"]),LOOPBACK_V4=/^127\.(?:\d{1,3})\.(?:\d{1,3})\.(?:\d{1,3})$/;function isLiteralLoopbackHost(hostname){return hostname==="[::1]"?!0:LOOPBACK_V4.test(hostname)?hostname.split(".").every(o=>Number(o)>=0&&Number(o)<=255):!1}function createReadOnlyEvmRpc(input){if(typeof input!="object"||input===null)throw new Error("createReadOnlyEvmRpc: input must be an object");if(typeof input.fetchImpl!="function")throw new Error("createReadOnlyEvmRpc: fetchImpl must be a function");let url=input.url,idCursor=0;return Object.freeze({url,async request(method,params){if(typeof method!="string"||!READ_ONLY_RPC_METHODS.includes(method))return{ok:!1,reason:"rpc_method_not_read_only",detail:`${String(method)} is not one of ${READ_ONLY_RPC_METHODS.join(", ")}. This client reads chains; it does not write to them. A relayer that needs to broadcast supplies its own \`SendRelayTransaction\`, which is where the key lives.`};let parsedUrl;try{parsedUrl=new URL(url)}catch{return{ok:!1,reason:"rpc_url_not_https",detail:`not a URL: ${String(url)}`}}if(!(parsedUrl.protocol==="https:"||parsedUrl.protocol==="http:"&&isLiteralLoopbackHost(parsedUrl.hostname)))return{ok:!1,reason:"rpc_url_not_https",detail:"a chain observation read over cleartext can be rewritten in flight by whoever is between, and this one decides whether to spend money. http is admitted ONLY to a literal loopback address (127.0.0.0/8 or [::1]), where there is nobody between; `localhost` is a name this check cannot resolve, so spell the literal."};let res;try{res=await input.fetchImpl(parsedUrl.toString(),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:++idCursor,method,params}),...input.signal?{signal:input.signal}:{}})}catch(err){return{ok:!1,reason:"rpc_unreachable",detail:err instanceof Error?err.message:String(err)}}let body;try{body=JSON.parse(await res.text())}catch{return{ok:!1,reason:"rpc_response_unreadable",detail:`${method} answered ${res.status} with a body that is not JSON.`}}if(typeof body!="object"||body===null||Array.isArray(body))return{ok:!1,reason:"rpc_result_malformed",detail:`${method}: not an object`};let o={...body};return o.error!==void 0&&o.error!==null?{ok:!1,reason:"rpc_error",detail:JSON.stringify(o.error)}:"result"in o?{ok:!0,result:o.result}:{ok:!1,reason:"rpc_result_malformed",detail:`${method}: 2xx JSON carrying neither result nor error`}}})}var ERROR_STRING_SELECTOR="0x08c379a0";function decodeRevertReason(data){if(typeof data!="string"||!data.startsWith(ERROR_STRING_SELECTOR))return null;let body=data.slice(ERROR_STRING_SELECTOR.length);if(body.length<128)return null;let offset=Number.parseInt(body.slice(0,64),16);if(!Number.isSafeInteger(offset)||offset<0)return null;let lenStart=offset*2;if(lenStart+64>body.length)return null;let length=Number.parseInt(body.slice(lenStart,lenStart+64),16);if(!Number.isSafeInteger(length)||length<0||length>4096)return null;let start=lenStart+64,hex=body.slice(start,start+length*2);if(hex.length!==length*2||!/^[0-9a-fA-F]*$/.test(hex))return null;let bytes=new Uint8Array(length);for(let i=0;i<length;i++)bytes[i]=Number.parseInt(hex.slice(i*2,i*2+2),16);try{return new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!1}).decode(bytes)}catch{return null}}function revertDataFromRpcError(detail){let match=detail.match(/0x08c379a0[0-9a-fA-F]*/);return match?match[0]:null}async function simulateTransaction(input){let res=await input.rpc.request("eth_call",[{from:input.from,to:input.request.to,data:input.request.data,value:"0x0"},"latest"]);if(res.ok)return{kind:"would_succeed",returned:typeof res.result=="string"?res.result:""};if(res.reason==="rpc_error"){let raw=revertDataFromRpcError(res.detail);if(raw!==null)return{kind:"would_revert",reason:decodeRevertReason(raw),raw};if(/revert|execution reverted/i.test(res.detail))return{kind:"would_revert",reason:null,raw:res.detail}}return{kind:"unavailable",detail:`${res.ok?"":res.reason}: ${res.ok?"":res.detail}`}}function hexToBigInt(value){if(typeof value!="string"||!/^0x[0-9a-fA-F]+$/.test(value))return null;try{return BigInt(value)}catch{return null}}async function estimateRelayCost(input){if(typeof input.gasLimitMarginPercent!="number"||!Number.isInteger(input.gasLimitMarginPercent)||input.gasLimitMarginPercent<0||input.gasLimitMarginPercent>500)return{ok:!1,reason:"estimate_unavailable",detail:"gasLimitMarginPercent must be an integer between 0 and 500."};let est=await input.rpc.request("eth_estimateGas",[{from:input.from,to:input.request.to,data:input.request.data,value:"0x0"}]),gas=est.ok?hexToBigInt(est.result):null;if(gas===null)return{ok:!1,reason:"estimate_unavailable",detail:est.ok?`unreadable estimate ${String(est.result)}`:`${est.reason}: ${est.detail}`};let priceRes=await input.rpc.request("eth_gasPrice",[]),gasPriceWei=priceRes.ok?hexToBigInt(priceRes.result):null;if(gasPriceWei===null)return{ok:!1,reason:"gas_price_unavailable",detail:priceRes.ok?`unreadable gas price ${String(priceRes.result)}`:`${priceRes.reason}: ${priceRes.detail}`};let balRes=await input.rpc.request("eth_getBalance",[input.from,"latest"]),relayerBalanceWei=balRes.ok?hexToBigInt(balRes.result):null;if(relayerBalanceWei===null)return{ok:!1,reason:"balance_unavailable",detail:balRes.ok?`unreadable balance ${String(balRes.result)}`:`${balRes.reason}: ${balRes.detail}`};let gasLimit=gas*BigInt(100+input.gasLimitMarginPercent)/BigInt(100),maxCostWei=gasLimit*gasPriceWei,cost=Object.freeze({gas,gasLimit,gasPriceWei,maxCostWei,relayerBalanceWei});return relayerBalanceWei<maxCostWei?{ok:!1,reason:"relayer_cannot_pay_gas",detail:`the relayer holds ${relayerBalanceWei} wei and the worst case is ${maxCostWei} wei (${gasLimit} gas at ${gasPriceWei} wei). Sending anyway produces a transaction the node drops, which is indistinguishable from a network fault to everything downstream.`}:{ok:!0,cost}}var RELAY_CHAINS=new Map;async function serialisePerRelayer(address,fn){let key=address.toLowerCase(),mine=(RELAY_CHAINS.get(key)??Promise.resolve()).then(fn,fn);RELAY_CHAINS.set(key,mine.then(()=>{},()=>{}));try{return await mine}finally{RELAY_CHAINS.get(key)===void 0&&RELAY_CHAINS.delete(key)}}var SINGLE_AUTHORIZATION_CALLDATA_RE=/^0x[0-9a-f]{584}$/,VERIFIED_AUTHORIZATION_SELECTORS=Object.freeze([TRANSFER_WITH_AUTHORIZATION_SELECTOR,RECEIVE_WITH_AUTHORIZATION_SELECTOR]);function beneficiaryFromCalldata(data){let w=data.slice(74,138);return w.length!==64||!/^0{24}[0-9a-f]{40}$/.test(w)?null:`0x${w.slice(24)}`}function validBeforeFromCalldata(data){let w=data.slice(266,330);if(w.length!==64||!/^[0-9a-f]{64}$/.test(w))return null;try{return BigInt(`0x${w}`)}catch{return null}}function checkRelayRemainingWindow(input){let validBefore=validBeforeFromCalldata(input.data);if(validBefore===null)return{ok:!1,remainingMs:BigInt(0),detail:"the calldata's fifth word is not a readable uint256, so this module cannot tell how much of the payment window is left. Refused rather than assumed."};let remainingMs=(validBefore-input.chainHeadSeconds)*BigInt(1e3);return remainingMs<BigInt(MIN_GRANT_TTL_MS)?{ok:!1,remainingMs,detail:`this authorization is valid until UNIX second ${validBefore} and the chain head reads ${input.chainHeadSeconds}, so ${remainingMs} ms of window remain. The rail will not call a payment settled below ${SESSION_RAIL_MIN_CONFIRMATIONS} confirmations, which is about ${SESSION_RAIL_MIN_CONFIRMATIONS*SESSION_RAIL_BLOCK_TIME_MS} ms of block time, and the protocol allows the three clocks involved to disagree by ${MAX_CLOCK_SKEW_MS} ms \u2014 ${MIN_GRANT_TTL_MS} ms in total. Relayed now, this payment would INCLUDE (the token only requires block.timestamp < validBefore), credit the payee in full, and reach depth after the grant it pays for has expired: redeemGrant answers \`expired\`, no journal row is written, and recoverTask answers \`no_session\`. The binding nonce is a function of the grant hash and USDC marks (authorizer, nonce) spent forever, so no correcting payment can ever satisfy that grant. Not sent \u2014 and the nonce is still free, so a hirer that reissues the hire can still be paid.`}:{ok:!0,remainingMs}}function checkSingleAuthorizationRelay(request,context={}){if(typeof request!="object"||request===null)return{ok:!1,detail:"the transaction request is not an object."};if(typeof request.chainId!="number"||!Number.isInteger(request.chainId))return{ok:!1,detail:`chainId ${String(request.chainId)} is not an integer.`};let token=null;for(let domain of EVM_USDC_EIP712_DOMAINS.values())domain.chainId===request.chainId&&(token=domain.verifyingContract.toLowerCase());if(token===null)return{ok:!1,detail:`no frozen USDC deployment is recorded for chain ${request.chainId}, so this module cannot tell a single authorization from a batch on it. Refused rather than assumed.`};if(typeof request.to!="string"||request.to.toLowerCase()!==token)return{ok:!1,detail:`this transaction calls ${String(request.to)}, and the only destination a single EIP-3009 authorization has on chain ${request.chainId} is the USDC deployment at ${token}. A different destination is an intermediary \u2014 a multicall, an aggregator, a batcher \u2014 and an intermediary is how several authorizations end up in one transaction.`};let data=typeof request.data=="string"?request.data.toLowerCase():"",selector=VERIFIED_AUTHORIZATION_SELECTORS.find(s=>data.startsWith(s))??null;if(selector===null)return{ok:!1,detail:`the calldata selector is ${data.slice(0,10)||"absent"} and the only calls this module relays are transferWithAuthorization (${TRANSFER_WITH_AUTHORIZATION_SELECTOR}) and receiveWithAuthorization (${RECEIVE_WITH_AUTHORIZATION_SELECTOR}). Their AuthorizationUsed log shape is the one the settlement verifier reads, and both have been measured against it end to end; no other entry point has been.`};if(!SINGLE_AUTHORIZATION_CALLDATA_RE.test(data))return{ok:!1,detail:`the calldata is ${data.length-2} hex characters and exactly 584 encode one EIP-3009 authorization (a 4-byte selector and nine static words \u2014 the same nine for both entry points). Surplus bytes are ignored by an ABI decoder and are how a second authorization rides along inside a call that looks well-formed.`};if(selector===RECEIVE_WITH_AUTHORIZATION_SELECTOR&&context.relayerAddress!==void 0){let relayer=typeof context.relayerAddress=="string"?context.relayerAddress.toLowerCase():"",beneficiary=beneficiaryFromCalldata(data);if(!ADDRESS_RE4.test(relayer))return{ok:!1,detail:`relayerAddress ${String(context.relayerAddress)} is not a 20-byte address, so this module cannot tell whether the receive call would be made by its own payee. Refused rather than assumed.`};if(beneficiary===null||beneficiary!==relayer)return{ok:!1,detail:`this is a receiveWithAuthorization paying ${String(beneficiary)} and it would be sent by ${relayer}. FiatTokenV2 requires msg.sender == to, so this call cannot execute \u2014 it would burn gas, emit no AuthorizationUsed log, and leave the provider holding a hash the adapter can never resolve. The receive variant is relayable only by the payee it names.`}}return request.value!=="0x0"?{ok:!1,detail:`this transaction carries native value ${String(request.value)}. USDC moves inside the call and never as value, so a non-zero value means the transaction is doing a second thing \u2014 which is the class this check exists to refuse.`}:{ok:!0}}var RelayRefusal=class extends Error{constructor(reason,detail){super(`${reason}: ${detail}`),this.name="RelayRefusal",this.reason=reason}},ADDRESS_RE4=/^0x[0-9a-fA-F]{40}$/,TX_HASH_RE2=/^0x[0-9a-f]{64}$/;function createPayeeRelayBroadcaster(input){if(typeof input!="object"||input===null)throw new Error("createPayeeRelayBroadcaster: input must be an object");if(typeof input.send!="function")throw new Error("createPayeeRelayBroadcaster: send must be a function");if(typeof input.relayerAddress!="string"||!ADDRESS_RE4.test(input.relayerAddress))throw new Error("createPayeeRelayBroadcaster: relayerAddress must be a 20-byte address");let relayer=input.relayerAddress.toLowerCase();return async function(request){let single=checkSingleAuthorizationRelay(request,{relayerAddress:relayer});if(!single.ok)throw new RelayRefusal("batched_relay_refused",`${single.detail} A transaction that consumes more than one EIP-3009 authorization is ambiguous about which grant each payment was for, and that ambiguity is fixed in the block forever \u2014 the settlement adapter answers \`unattributable\`, the payers stay debited, and no provider in the batch can redeem. Relay one authorization per transaction. Not sent.`);let idRes=await input.rpc.request("eth_chainId",[]),observed=idRes.ok?hexToBigInt(idRes.result):null;if(observed===null)throw new RelayRefusal("chain_id_unavailable",idRes.ok?`unreadable chainId ${String(idRes.result)}`:`${idRes.reason}: ${idRes.detail}`);if(observed!==BigInt(request.chainId))throw new RelayRefusal("chain_id_mismatch",`the authorization is for chain ${request.chainId} and this RPC answers ${observed}. The chain id is inside the EIP-712 domain separator, so this signature is not valid on the network this node serves.`);let headRes=await input.rpc.request("eth_getBlockByNumber",["latest",!1]),headTs=headRes.ok&&typeof headRes.result=="object"&&headRes.result!==null?hexToBigInt(headRes.result.timestamp):null;if(headTs===null)throw new RelayRefusal("chain_time_unavailable",headRes.ok?`the node's latest block carries no readable timestamp (${JSON.stringify(headRes.result).slice(0,120)})`:`${headRes.reason}: ${headRes.detail}`);let window=checkRelayRemainingWindow({data:String(request.data).toLowerCase(),chainHeadSeconds:headTs});if(!window.ok)throw new RelayRefusal("relay_window_too_short",window.detail);let sim=await simulateTransaction({rpc:input.rpc,request,from:relayer});if(sim.kind==="would_revert")throw new RelayRefusal("simulation_reverted",`eth_call reverted with ${sim.reason??"no reason string"} (${sim.raw.slice(0,200)}). A reverted transferWithAuthorization emits no AuthorizationUsed log, so the settlement binding would have nowhere to live and the redemption could never resolve. Not sent.`);if(sim.kind==="unavailable")throw new RelayRefusal("simulation_unavailable",`${sim.detail}. The node did not answer, so nothing was ruled out. Not sent.`);let cost=await estimateRelayCost({rpc:input.rpc,request,from:relayer,gasLimitMarginPercent:input.gasLimitMarginPercent});if(!cost.ok)throw new RelayRefusal(cost.reason,cost.detail);let hash;try{hash=await serialisePerRelayer(relayer,()=>Promise.resolve(input.send({from:relayer,to:request.to,data:request.data,value:request.value,chainId:request.chainId,gasLimit:cost.cost.gasLimit,gasPriceWei:cost.cost.gasPriceWei})))}catch(err){throw new RelayRefusal("send_threw",`the injected sender threw. THIS IS NOT PROOF NOTHING HAPPENED \u2014 a timeout after the transaction reached a mempool looks identical from here. (${err instanceof Error?err.message:String(err)})`)}if(typeof hash!="string"||!TX_HASH_RE2.test(hash.toLowerCase()))throw new RelayRefusal("send_returned_no_hash","the sender returned something that is not a `0x` + 64 hex transaction hash, so there is nothing to present to the provider as evidence.");return hash.toLowerCase()}}var AUTHORIZATION_USED_TOPIC0="0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5",HEX32_NO_PREFIX_RE=/^[0-9a-f]{64}$/;async function resolveSettlementTransaction(input){if(typeof input!="object"||input===null)return{kind:"unavailable",detail:"resolveSettlementTransaction: input must be an object"};if(typeof input.chainId!="number"||!Number.isInteger(input.chainId))return{kind:"unavailable",detail:`chainId ${String(input.chainId)} is not an integer.`};let token=null;for(let domain of EVM_USDC_EIP712_DOMAINS.values())domain.chainId===input.chainId&&(token=domain.verifyingContract.toLowerCase());if(token===null)return{kind:"unavailable",detail:`no frozen USDC deployment is recorded for chain ${input.chainId}, so there is no emitter to pin this query to. Refused rather than asking every contract on the chain.`};let authorizer=typeof input.authorizer=="string"?input.authorizer.toLowerCase():"";if(!ADDRESS_RE4.test(authorizer))return{kind:"unavailable",detail:`authorizer ${String(input.authorizer)} is not a 20-byte address.`};let ref=typeof input.bindingReference=="string"?input.bindingReference.toLowerCase().replace(/^0x/,""):"";if(!HEX32_NO_PREFIX_RE.test(ref))return{kind:"unavailable",detail:`bindingReference ${String(input.bindingReference)} is not 32 bytes of lowercase hex. It is settlementBindingReference(grantHash) and nothing else.`};if(typeof input.fromBlock!="bigint"||input.fromBlock<BigInt(0))return{kind:"unavailable",detail:"fromBlock is required and must be a non-negative bigint. There is no default: a guessed window either misses a payment that exists or asks for an unbounded scan."};let res=await input.rpc.request("eth_getLogs",[{address:token,topics:[AUTHORIZATION_USED_TOPIC0,`0x${"0".repeat(24)}${authorizer.slice(2)}`,`0x${ref}`],fromBlock:`0x${input.fromBlock.toString(16)}`,toBlock:"latest"}]);if(!res.ok)return{kind:"unavailable",detail:`${res.reason}: ${res.detail}. Some providers cap eth_getLogs ranges or refuse a wide fromBlock with toBlock:latest; a narrower window from the same anchor may answer. Nothing was learned, so nothing should be concluded.`};if(!Array.isArray(res.result))return{kind:"unavailable",detail:`eth_getLogs answered ${typeof res.result}, not an array of logs.`};if(res.result.length===0)return{kind:"not_consumed"};if(res.result.length>1)return{kind:"impossible",detail:`${res.result.length} AuthorizationUsed logs carry authorizer ${authorizer} and nonce 0x${ref} on ${token}. FiatTokenV2 marks that pair consumed before it transfers, so at most one transaction can ever emit it. This node is wrong about something; refusing rather than choosing one of them.`};let log=res.result[0],hash=typeof log.transactionHash=="string"?log.transactionHash.toLowerCase():"";if(!TX_HASH_RE2.test(hash))return{kind:"unavailable",detail:`the log carries transactionHash ${String(log.transactionHash)}, which is not a hash.`};let blockNumber=hexToBigInt(log.blockNumber);return blockNumber===null?{kind:"unavailable",detail:`the log carries blockNumber ${String(log.blockNumber)}. A pending log has no block, and a payment that is not in a block is not a payment yet.`}:{kind:"found",transactionHash:hash,blockNumber}}export{AUTHORIZATION_ENTRY_POINTS,AUTHORIZATION_KEYS,AUTHORIZATION_USED_TOPIC0,CAPSULE_NONCE_LENGTH,EVM_USDC_EIP712_DOMAINS,FACILITATOR_PREFLIGHT_SCHEMA,FORBIDDEN_RPC_METHODS,KNOWN_ASSET_TRANSFER_METHODS,MAX_BRIEF_LENGTH,MAX_CLOCK_SKEW_MS,MAX_GRANT_TTL_MS,MAX_OFFER_TTL_MS,MAX_RESULT_LENGTH,MAX_SERVICE_REF_LENGTH,MIN_GRANT_TTL_MS,MIN_NONCE_LENGTH,PAYMENT_AUTHORIZATION_SCHEME,PREFLIGHT_DEFAULT_CHAIN,PROVIDER_MANIFEST_KEYS,PROVIDER_MANIFEST_PATH,PROVIDER_MANIFEST_SCHEMA,READ_ONLY_RPC_METHODS,RECEIVE_WITH_AUTHORIZATION_SELECTOR,RECEIVE_WITH_AUTHORIZATION_TYPEHASH,REDEMPTION_ATTESTATION_SCHEMA,REQUIRED_ASSET_TRANSFER_METHOD,RelayRefusal,SESSION_PATHS,SESSION_PROVIDER_PROOF_HEADER,SESSION_PROVIDER_PROOF_MAX_WINDOW_MS,SESSION_PROVIDER_PROOF_SCHEMA,SESSION_RAIL_BLOCK_TIME_MS,SESSION_RAIL_MIN_CONFIRMATIONS,SETTLEMENT_BINDING_DOMAIN,SessionCryptoUnavailableError,SessionTransportError,SessionUsageError,TRANSFER_WITH_AUTHORIZATION_SELECTOR,TRANSFER_WITH_AUTHORIZATION_TYPEHASH,X402_SESSION_EVIDENCE_SCHEMA,X402_SESSION_USDC_BY_CHAIN,X402_V1_NETWORK_ALIASES,acceptHire,assembleSignedReceiveAuthorization,assembleSignedTransferAuthorization,authenticateHireAcceptance,authorizationEntryPoint,authorizationValidBeforeFor,bindAuthorizationToGrant,buildHire,buildHireMessage,buildReceiveAuthorizationTypedData,buildReceivePaymentAuthorization,buildReceiveWithAuthorizationCalldata,buildRecoveryRequest,buildRedemptionProofHeader,buildSettlementHint,buildTransferAuthorizationTypedData,buildTransferPaymentAuthorization,buildTransferWithAuthorizationCalldata,buildX402PaymentPayload,buildX402PaymentRequirements,caip2Of,canonicalBytes,canonicalize,checkSingleAuthorizationRelay,compareDecimalStrings,createFacilitatorSubmitter,createPayeeRelayBroadcaster,createReadOnlyEvmRpc,createSelfSubmitter,decideFromSupported,decodeRevertReason,deriveDidFromSigningKey,destroySessionKey,encodeSessionProviderProof,envelopeHash,estimateRelayCost,exportSessionKeyBytes,fetchVerifiedProvider,frameBucketSize,hashArtifact,importSessionKey,isAllZero,isCaip10,isCaip19,isCaip2,isPositiveDecimalString,isSessionParty,isVerifiedProvider,openBrief,openDeliveredResult,payForGrant,postDeliver,postReattest,postRecover,postRedeem,preflightAdmitsPayment,preflightFacilitator,privateHire,recoverResult,resolveSettlementTransaction,revertDataFromRpcError,reviewHire,sealCapsule,sealTaskResult,sessionProviderProofEnvelope,settleUrlFor,settlementBindingReference,settlementNonce,sha256Hex,signCanonical,signDelivery,signHireAuthorization,signReceiveAuthorization,signTransferAuthorization,simulateTransaction,submitHire,submitSettlementHint,supportedUrlFor,timestampMs,unsealBody,validateAcceptance,validateAuthorizationShape,validateCapsuleShape,validateGrant,validateOffer,validateRecoveryRequest,validateRedemptionAttestation,validateResultCapsuleShape,validateX402SessionEvidence,verifyDeliveryReceipt,verifyDetached,verifyProvider,webCryptoEntropy,x402SessionAccountCaip10,x402SessionAssetCaip19,x402SessionEvidence};
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@voidly/session",
3
+ "version": "1.0.0",
4
+ "description": "Client for the Voidly Pay session rail: private hire, verify-only settlement on Base. The rail never holds, custodies or submits funds — the payee relays its own settlement and the verifier's only I/O is read-only JSON-RPC.",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/voidly-ai/session.git"
9
+ },
10
+ "homepage": "https://github.com/voidly-ai/session#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/voidly-ai/session/issues"
13
+ },
14
+ "main": "./dist/index.mjs",
15
+ "module": "./dist/index.mjs",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "default": "./dist/index.mjs"
22
+ },
23
+ "./break-even": {
24
+ "types": "./dist/breakEven.d.ts",
25
+ "import": "./dist/breakEven.mjs",
26
+ "default": "./dist/breakEven.mjs"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "LICENSE",
34
+ "NOTICE"
35
+ ],
36
+ "sideEffects": false,
37
+ "scripts": {
38
+ "build": "node scripts/build.mjs",
39
+ "gate": "node scripts/check-tarball.mjs",
40
+ "gate:narrative": "node ../scripts/check-assembly-seal.mjs",
41
+ "gate:origin": "node scripts/check-origin.mjs",
42
+ "prepublishOnly": "npm run build && npm run gate && npm run gate:narrative && npm run gate:origin",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc --noEmit && tsc --noEmit -p scripts/tsconfig.json",
45
+ "dry-run": "node scripts/dry-run-base-mainnet-payment.mjs"
46
+ },
47
+ "dependencies": {
48
+ "tweetnacl": "^1.0.3",
49
+ "tweetnacl-util": "^0.15.1"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^20.10.0",
53
+ "esbuild": "^0.28.1",
54
+ "ethers": "^6.17.0",
55
+ "typescript": "^5.3.3",
56
+ "vitest": "^3.2.6"
57
+ },
58
+ "engines": {
59
+ "node": ">=18"
60
+ },
61
+ "license": "Apache-2.0"
62
+ }