settld 0.1.2 → 0.2.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/README.md +93 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +71 -0
- package/conformance/kernel-v0/README.md +7 -0
- package/conformance/kernel-v0/run.mjs +292 -4
- package/docs/ACCESS.md +57 -0
- package/docs/ADOPTION_CHECKLIST.md +44 -0
- package/docs/ALERTS.md +198 -0
- package/docs/ARCHITECTURE.md +69 -0
- package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
- package/docs/ARTIFACTS.md +60 -0
- package/docs/CERTIFICATION_CHECKLIST.md +33 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
- package/docs/CONFIG.md +297 -0
- package/docs/CONTRACTS_APIS.md +23 -0
- package/docs/DEPRECATION.md +31 -0
- package/docs/DOMAIN_MODEL.md +92 -0
- package/docs/EVENT_ENVELOPE.md +53 -0
- package/docs/FINANCE_PACK_FORMAT.md +53 -0
- package/docs/INCIDENT_TAXONOMY.md +30 -0
- package/docs/JOB_STATE_MACHINE.md +66 -0
- package/docs/KERNEL_COMPATIBLE.md +60 -0
- package/docs/KERNEL_V0.md +40 -0
- package/docs/KEY_ROTATION.md +80 -0
- package/docs/LEDGER.md +82 -0
- package/docs/LIVENESS.md +76 -0
- package/docs/MVP_BUILD_ORDER.md +36 -0
- package/docs/ONCALL_PLAYBOOK.md +39 -0
- package/docs/OPERATIONS_SIGNING.md +20 -0
- package/docs/OVERVIEW.md +190 -0
- package/docs/PERF_BASELINE.md +85 -0
- package/docs/PRD.md +77 -0
- package/docs/QUICKSTART_KERNEL_V0.md +96 -0
- package/docs/QUICKSTART_MCP.md +377 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
- package/docs/QUICKSTART_SDK.md +125 -0
- package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
- package/docs/QUICKSTART_VERIFY.md +54 -0
- package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
- package/docs/README.md +33 -0
- package/docs/RELEASE_CHECKLIST.md +182 -0
- package/docs/RELEASING.md +82 -0
- package/docs/REPO_SETTINGS.md +37 -0
- package/docs/RUNBOOK.md +86 -0
- package/docs/SKILLS.md +42 -0
- package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
- package/docs/SLO.md +131 -0
- package/docs/SUMMARY.md +17 -0
- package/docs/SUPPORT.md +31 -0
- package/docs/THREAT_MODEL.md +36 -0
- package/docs/TRUST.md +59 -0
- package/docs/WORKFLOW.md +35 -0
- package/docs/X402_BATCH_SETTLEMENT.md +126 -0
- package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
- package/docs/examples/x402-provider-payout-registry.example.json +14 -0
- package/docs/gitbook/README.md +64 -0
- package/docs/gitbook/SETUP.md +25 -0
- package/docs/gitbook/SUMMARY.md +15 -0
- package/docs/gitbook/api-reference.md +73 -0
- package/docs/gitbook/closepacks.md +55 -0
- package/docs/gitbook/conformance.md +59 -0
- package/docs/gitbook/core-primitives.md +85 -0
- package/docs/gitbook/dispute-lifecycle.md +33 -0
- package/docs/gitbook/faq.md +21 -0
- package/docs/gitbook/guides.md +49 -0
- package/docs/gitbook/operations-runbook.md +36 -0
- package/docs/gitbook/quickstart.md +103 -0
- package/docs/gitbook/replay-and-audit.md +30 -0
- package/docs/gitbook/sdk-reference.md +35 -0
- package/docs/gitbook/security-model.md +58 -0
- package/docs/integrations/README.md +15 -0
- package/docs/integrations/github-actions-verify.yml +31 -0
- package/docs/integrations/github-actions.md +34 -0
- package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
- package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
- package/docs/kernel-compatible/capabilities.json +36 -0
- package/docs/marketing/agent-commerce-substrate.md +78 -0
- package/docs/marketing/hn-repost-2026-02-17.md +102 -0
- package/docs/marketing/show-hn-post.md +45 -0
- package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
- package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
- package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
- package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
- package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
- package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
- package/docs/ops/R1_SLOS.md +66 -0
- package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
- package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
- package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
- package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
- package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
- package/docs/ops/tool-call-disputes-holdback.md +52 -0
- package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
- package/docs/pilot-kit/README.md +29 -0
- package/docs/pilot-kit/architecture-one-pager.md +48 -0
- package/docs/pilot-kit/buyer-email.txt +19 -0
- package/docs/pilot-kit/buyer-one-pager.md +31 -0
- package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
- package/docs/pilot-kit/offline-verify.md +33 -0
- package/docs/pilot-kit/procurement-one-pager.md +50 -0
- package/docs/pilot-kit/rfp-clause.md +46 -0
- package/docs/pilot-kit/roi-calculator-template.csv +2 -0
- package/docs/pilot-kit/security-qa.md +153 -0
- package/docs/pilot-kit/security-summary.md +35 -0
- package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/AcceptanceCriteria.v1.md +17 -0
- package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
- package/docs/spec/AgentEvent.v1.md +47 -0
- package/docs/spec/AgentIdentity.v1.md +62 -0
- package/docs/spec/AgentPassport.v1.md +95 -0
- package/docs/spec/AgentReputation.v1.md +59 -0
- package/docs/spec/AgentReputation.v2.md +52 -0
- package/docs/spec/AgentRun.v1.md +47 -0
- package/docs/spec/AgentRunSettlement.v1.md +52 -0
- package/docs/spec/AgentWallet.v1.md +43 -0
- package/docs/spec/AgreementDelegation.v1.md +109 -0
- package/docs/spec/ArbitrationCase.v1.md +67 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/ArbitrationVerdict.v1.md +60 -0
- package/docs/spec/BundleHeadAttestation.v1.md +32 -0
- package/docs/spec/CANONICAL_JSON.md +31 -0
- package/docs/spec/CRYPTOGRAPHY.md +61 -0
- package/docs/spec/ClosePack.v1.md +49 -0
- package/docs/spec/ClosePackManifest.v1.md +24 -0
- package/docs/spec/DelegationGrant.v1.md +90 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
- package/docs/spec/ERRORS.md +76 -0
- package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
- package/docs/spec/EvidenceIndex.v1.md +20 -0
- package/docs/spec/ExecutionIntent.v1.md +90 -0
- package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
- package/docs/spec/FundingHold.v1.md +60 -0
- package/docs/spec/GovernancePolicy.v1.md +34 -0
- package/docs/spec/GovernancePolicy.v2.md +30 -0
- package/docs/spec/INVARIANTS.md +389 -0
- package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
- package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
- package/docs/spec/InvoiceClaim.v1.md +11 -0
- package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
- package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
- package/docs/spec/MarketplaceOffer.v2.md +54 -0
- package/docs/spec/MeteringReport.v1.md +18 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PRODUCER_ERRORS.md +42 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/PricingMatrix.v1.md +20 -0
- package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
- package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
- package/docs/spec/ProduceCliOutput.v1.md +46 -0
- package/docs/spec/ProofBundleManifest.v1.md +24 -0
- package/docs/spec/README.md +109 -0
- package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
- package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
- package/docs/spec/REMOTE_SIGNER.md +66 -0
- package/docs/spec/ReleaseIndex.v1.md +32 -0
- package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
- package/docs/spec/ReleaseTrust.v1.md +13 -0
- package/docs/spec/ReleaseTrust.v2.md +26 -0
- package/docs/spec/RemoteSignerRequest.v1.md +21 -0
- package/docs/spec/RemoteSignerResponse.v1.md +16 -0
- package/docs/spec/ReputationEvent.v1.md +63 -0
- package/docs/spec/RevocationList.v1.md +28 -0
- package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
- package/docs/spec/STRICTNESS.md +68 -0
- package/docs/spec/SUPPLY_CHAIN.md +33 -0
- package/docs/spec/SettlementAdjustment.v1.md +45 -0
- package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
- package/docs/spec/SettlementDecisionReport.v1.md +44 -0
- package/docs/spec/SettlementKernel.v1.md +59 -0
- package/docs/spec/SettlementReceipt.v1.md +63 -0
- package/docs/spec/SlaDefinition.v1.md +24 -0
- package/docs/spec/SlaEvaluation.v1.md +12 -0
- package/docs/spec/THREAT_MODEL.md +113 -0
- package/docs/spec/TOOL_PROVENANCE.md +30 -0
- package/docs/spec/TRUST_ANCHORS.md +84 -0
- package/docs/spec/TenantSettings.v1.md +90 -0
- package/docs/spec/TenantSettings.v2.md +99 -0
- package/docs/spec/TimestampProof.v1.md +25 -0
- package/docs/spec/ToolCallAgreement.v1.md +34 -0
- package/docs/spec/ToolCallEvidence.v1.md +47 -0
- package/docs/spec/ToolManifest.v1.md +47 -0
- package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
- package/docs/spec/VERSIONING.md +107 -0
- package/docs/spec/VerificationReport.v1.md +50 -0
- package/docs/spec/VerifyAboutOutput.v1.md +10 -0
- package/docs/spec/VerifyCliOutput.v1.md +28 -0
- package/docs/spec/WARNINGS.md +83 -0
- package/docs/spec/error-codes.v1.txt +285 -0
- package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
- package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
- package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
- package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
- package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
- package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
- package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
- package/docs/spec/examples/release_index_v1.example.json +15 -0
- package/docs/spec/examples/release_trust_v1.example.json +7 -0
- package/docs/spec/examples/release_trust_v2.example.json +22 -0
- package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
- package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
- package/docs/spec/examples/reputation_event_v1.example.json +29 -0
- package/docs/spec/examples/verification_report_v1.example.json +24 -0
- package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
- package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
- package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
- package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
- package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
- package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
- package/docs/spec/producer-error-codes.v1.txt +14 -0
- package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
- package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
- package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
- package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
- package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
- package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
- package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
- package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
- package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
- package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
- package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
- package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
- package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
- package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
- package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
- package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
- package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
- package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
- package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
- package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
- package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
- package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
- package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
- package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
- package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
- package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
- package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
- package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
- package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
- package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
- package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
- package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
- package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
- package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
- package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
- package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
- package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
- package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
- package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
- package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
- package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
- package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
- package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
- package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
- package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
- package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
- package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
- package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
- package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
- package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
- package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
- package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
- package/docs/spec/x402-error-codes.v1.txt +35 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +53 -6
- package/scripts/acceptance/full-stack.mjs +734 -0
- package/scripts/acceptance/full-stack.sh +99 -0
- package/scripts/audit/build-audit-packet.mjs +242 -0
- package/scripts/backup-pg.sh +45 -0
- package/scripts/backup-restore/README.md +18 -0
- package/scripts/backup-restore/capture-state.mjs +130 -0
- package/scripts/backup-restore/client.mjs +97 -0
- package/scripts/backup-restore/seed-workload.mjs +235 -0
- package/scripts/backup-restore/verify-state.mjs +139 -0
- package/scripts/backup-restore-test.sh +217 -0
- package/scripts/chaos.js +221 -0
- package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
- package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
- package/scripts/ci/changelog-guard.mjs +145 -0
- package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
- package/scripts/ci/check-secret-hygiene.mjs +78 -0
- package/scripts/ci/check-version-consistency.mjs +42 -0
- package/scripts/ci/cli-pack-smoke.mjs +160 -0
- package/scripts/ci/flake-budget-guard.mjs +68 -0
- package/scripts/ci/generate-error-codes.mjs +54 -0
- package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
- package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
- package/scripts/ci/npm-pack-smoke.mjs +454 -0
- package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
- package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
- package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
- package/scripts/ci/run-go-live-gate.mjs +150 -0
- package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
- package/scripts/ci/runtime-import-smoke.mjs +58 -0
- package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
- package/scripts/closepack/lib.mjs +286 -0
- package/scripts/collect-debug.sh +263 -0
- package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
- package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
- package/scripts/demo/delivery-robot/generate.mjs +377 -0
- package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
- package/scripts/demo/magic-link-first-green.mjs +118 -0
- package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
- package/scripts/demo/mcp-paid-exa.mjs +1110 -0
- package/scripts/dev/billing-doctor.sh +145 -0
- package/scripts/dev/billing-smoke-prod.sh +219 -0
- package/scripts/dev/billing-webhook-replay.sh +161 -0
- package/scripts/dev/env.dev.example +29 -0
- package/scripts/dev/env.sh +37 -0
- package/scripts/dev/new-sdk-key.sh +81 -0
- package/scripts/dev/sdk-first-run.sh +21 -0
- package/scripts/dev/smoke-x402-gateway.sh +115 -0
- package/scripts/dev/start-api.sh +24 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
- package/scripts/examples/sdk-first-paid-rfq.py +105 -0
- package/scripts/examples/sdk-first-verified-run.mjs +85 -0
- package/scripts/examples/sdk-first-verified-run.py +99 -0
- package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
- package/scripts/examples/sdk-tenant-analytics.py +118 -0
- package/scripts/finance-pack/bundle.mjs +284 -0
- package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
- package/scripts/governance/export.mjs +169 -0
- package/scripts/load/delivery-stress.k6.js +183 -0
- package/scripts/load/ingest-burst.k6.js +236 -0
- package/scripts/load/run-delivery-load.js +66 -0
- package/scripts/load/webhook-receiver.js +131 -0
- package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
- package/scripts/mcp/probe.mjs +238 -0
- package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
- package/scripts/mcp/settld-mcp-server.mjs +1511 -0
- package/scripts/openapi/write.mjs +13 -0
- package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
- package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
- package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
- package/scripts/ops/design-partner-run-packet.mjs +466 -0
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
- package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
- package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
- package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/pilot/finance-pack.mjs +495 -0
- package/scripts/pilot/fixtures/robot-keypair.json +4 -0
- package/scripts/pilot/fixtures/server-signer.json +4 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/proof-bundle/job.mjs +109 -0
- package/scripts/proof-bundle/lib.mjs +92 -0
- package/scripts/proof-bundle/month.mjs +103 -0
- package/scripts/provider/conformance-run.mjs +159 -0
- package/scripts/provider/keys-generate.mjs +135 -0
- package/scripts/provider/publish.mjs +420 -0
- package/scripts/quickstart/x402.mjs +334 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/release/build-artifacts.mjs +181 -0
- package/scripts/release/generate-release-index.mjs +112 -0
- package/scripts/release/release-index-lib.mjs +232 -0
- package/scripts/release/sign-release-index.mjs +85 -0
- package/scripts/release/validate-release-assets.mjs +170 -0
- package/scripts/release/verify-release.mjs +261 -0
- package/scripts/restore-pg.sh +34 -0
- package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
- package/scripts/sdk/smoke-python.py +30 -0
- package/scripts/sdk/smoke.mjs +16 -0
- package/scripts/settlement/x402-batch-worker.mjs +1091 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +239 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +59 -0
- package/scripts/trust/validate-trust-file.mjs +57 -0
- package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
- package/scripts/trust-config/wizard.mjs +161 -0
- package/scripts/vendor-contract-test-lib.mjs +182 -0
- package/scripts/vendor-contract-test.mjs +55 -0
- package/scripts/vercel/build-mkdocs.sh +9 -0
- package/scripts/vercel/ignore-mkdocs.sh +25 -0
- package/scripts/vercel/install-mkdocs.sh +6 -0
- package/scripts/verify-pg.js +217 -0
- package/scripts/x402/receipt-verify.mjs +289 -0
- package/services/finance-sink/src/dedupe-store.js +29 -6
- package/services/receiver/src/dedupe-store.js +29 -5
- package/services/x402-gateway/Dockerfile +13 -0
- package/services/x402-gateway/README.md +58 -0
- package/services/x402-gateway/examples/upstream-mock.js +337 -0
- package/services/x402-gateway/src/server.js +1058 -0
- package/src/api/app.js +34658 -16940
- package/src/api/maintenance.js +70 -0
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +1778 -70
- package/src/api/persistence.js +456 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1581 -62
- package/src/api/workers/deliveries.js +99 -4
- package/src/api/workers/insolvency-sweep.js +159 -0
- package/src/core/agent-card.js +69 -0
- package/src/core/agent-wallets.js +231 -0
- package/src/core/agreement-delegation.js +549 -0
- package/src/core/billing-plans.js +40 -6
- package/src/core/circle-reserve-adapter.js +845 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/maintenance-locks.js +1 -0
- package/src/core/operator-action.js +303 -0
- package/src/core/paid-tool-manifest.js +318 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/provider-publish-conformance.js +525 -0
- package/src/core/provider-publish-proof.js +396 -0
- package/src/core/provider-quote-signature.js +170 -0
- package/src/core/settld-keys.js +112 -0
- package/src/core/settld-pay-token.js +344 -0
- package/src/core/settlement-kernel.js +239 -2
- package/src/core/settlement-verifier.js +335 -0
- package/src/core/tool-call-agreement.js +112 -0
- package/src/core/tool-call-evidence.js +144 -0
- package/src/core/tool-provider-signature.js +98 -0
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/core/x402-escalation-override.js +258 -0
- package/src/core/x402-gate.js +118 -0
- package/src/core/x402-provider-refund-decision.js +220 -0
- package/src/core/x402-receipt-verifier.js +708 -0
- package/src/core/x402-reversal-command.js +251 -0
- package/src/core/x402-wallet-issuer-decision.js +252 -0
- package/src/core/zk-verifier.js +300 -0
- package/src/db/migrations/029_reputation_event_index.sql +54 -0
- package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
- package/src/db/pg.js +18 -7
- package/src/db/store-pg.js +1508 -111
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { canonicalJsonStringify, normalizeForCanonicalJson } from "./canonical-json.js";
|
|
2
|
+
import { keyIdFromPublicKeyPem, sha256Hex, signHashHexEd25519, verifyHashHexEd25519 } from "./crypto.js";
|
|
3
|
+
|
|
4
|
+
export const X402_ESCALATION_OVERRIDE_TOKEN_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
function assertNonEmptyString(value, name) {
|
|
7
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
|
|
8
|
+
return String(value).trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function assertOptionalId(value, name, { max = 200 } = {}) {
|
|
12
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
13
|
+
const out = String(value).trim();
|
|
14
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
15
|
+
if (!/^[A-Za-z0-9:._/-]+$/.test(out)) throw new TypeError(`${name} must match ^[A-Za-z0-9:._/-]+$`);
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function assertOptionalSha256Hex(value, name) {
|
|
20
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
21
|
+
const out = String(value).trim().toLowerCase();
|
|
22
|
+
if (!/^[0-9a-f]{64}$/.test(out)) throw new TypeError(`${name} must be sha256 hex`);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertPositiveSafeInt(value, name) {
|
|
27
|
+
const n = Number(value);
|
|
28
|
+
if (!Number.isSafeInteger(n) || n <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeCurrency(value, name) {
|
|
33
|
+
const raw = typeof value === "string" && value.trim() !== "" ? value : "USD";
|
|
34
|
+
const out = raw.trim().toUpperCase();
|
|
35
|
+
if (!/^[A-Z][A-Z0-9_]{2,11}$/.test(out)) throw new TypeError(`${name} must match ^[A-Z][A-Z0-9_]{2,11}$`);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeOptionalRequestBindingMode(value, name) {
|
|
40
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
41
|
+
const mode = String(value).trim().toLowerCase();
|
|
42
|
+
if (mode !== "strict") throw new TypeError(`${name} must be strict when provided`);
|
|
43
|
+
return mode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeUnixSeconds(value, name) {
|
|
47
|
+
const n = Number(value);
|
|
48
|
+
if (!Number.isSafeInteger(n) || n <= 0) throw new TypeError(`${name} must be a positive safe integer unix timestamp`);
|
|
49
|
+
return n;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function decodeEnvelope(token) {
|
|
53
|
+
const raw = assertNonEmptyString(token, "token");
|
|
54
|
+
let decoded = null;
|
|
55
|
+
try {
|
|
56
|
+
decoded = Buffer.from(raw, "base64url").toString("utf8");
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new TypeError(`token is not valid base64url: ${err?.message ?? String(err ?? "")}`);
|
|
59
|
+
}
|
|
60
|
+
let parsed = null;
|
|
61
|
+
try {
|
|
62
|
+
parsed = JSON.parse(decoded);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
throw new TypeError(`token is not valid JSON envelope: ${err?.message ?? String(err ?? "")}`);
|
|
65
|
+
}
|
|
66
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new TypeError("token envelope must be an object");
|
|
67
|
+
if (Number(parsed.v) !== X402_ESCALATION_OVERRIDE_TOKEN_VERSION) {
|
|
68
|
+
throw new TypeError(`token envelope version must be ${X402_ESCALATION_OVERRIDE_TOKEN_VERSION}`);
|
|
69
|
+
}
|
|
70
|
+
const kid = assertNonEmptyString(parsed.kid, "token.kid");
|
|
71
|
+
if (!parsed.payload || typeof parsed.payload !== "object" || Array.isArray(parsed.payload)) {
|
|
72
|
+
throw new TypeError("token.payload must be an object");
|
|
73
|
+
}
|
|
74
|
+
const sig = assertNonEmptyString(parsed.sig, "token.sig");
|
|
75
|
+
return { envelope: parsed, kid, payload: parsed.payload, sig };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function buildX402EscalationOverridePayloadV1({
|
|
79
|
+
overrideId,
|
|
80
|
+
escalationId,
|
|
81
|
+
gateId,
|
|
82
|
+
sponsorRef,
|
|
83
|
+
sponsorWalletRef,
|
|
84
|
+
policyRef,
|
|
85
|
+
policyVersion,
|
|
86
|
+
policyFingerprint,
|
|
87
|
+
amountCents,
|
|
88
|
+
currency = "USD",
|
|
89
|
+
payeeProviderId,
|
|
90
|
+
quoteId = null,
|
|
91
|
+
quoteSha256 = null,
|
|
92
|
+
requestBindingMode = null,
|
|
93
|
+
requestBindingSha256 = null,
|
|
94
|
+
idempotencyKey,
|
|
95
|
+
nonce,
|
|
96
|
+
iat,
|
|
97
|
+
exp
|
|
98
|
+
} = {}) {
|
|
99
|
+
const normalizedIat = normalizeUnixSeconds(iat, "iat");
|
|
100
|
+
const normalizedExp = normalizeUnixSeconds(exp, "exp");
|
|
101
|
+
if (normalizedExp <= normalizedIat) throw new TypeError("exp must be greater than iat");
|
|
102
|
+
const normalizedRequestBindingSha256 = assertOptionalSha256Hex(requestBindingSha256, "requestBindingSha256");
|
|
103
|
+
const normalizedRequestBindingMode =
|
|
104
|
+
normalizeOptionalRequestBindingMode(requestBindingMode, "requestBindingMode") ??
|
|
105
|
+
(normalizedRequestBindingSha256 ? "strict" : null);
|
|
106
|
+
if (normalizedRequestBindingMode === "strict" && !normalizedRequestBindingSha256) {
|
|
107
|
+
throw new TypeError("requestBindingSha256 is required when requestBindingMode=strict");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return normalizeForCanonicalJson(
|
|
111
|
+
{
|
|
112
|
+
schemaVersion: "X402EscalationOverridePayload.v1",
|
|
113
|
+
overrideId: assertOptionalId(overrideId, "overrideId", { max: 200 }) ?? (() => {
|
|
114
|
+
throw new TypeError("overrideId is required");
|
|
115
|
+
})(),
|
|
116
|
+
escalationId: assertOptionalId(escalationId, "escalationId", { max: 200 }) ?? (() => {
|
|
117
|
+
throw new TypeError("escalationId is required");
|
|
118
|
+
})(),
|
|
119
|
+
gateId: assertOptionalId(gateId, "gateId", { max: 200 }) ?? (() => {
|
|
120
|
+
throw new TypeError("gateId is required");
|
|
121
|
+
})(),
|
|
122
|
+
sponsorRef: assertOptionalId(sponsorRef, "sponsorRef", { max: 200 }) ?? (() => {
|
|
123
|
+
throw new TypeError("sponsorRef is required");
|
|
124
|
+
})(),
|
|
125
|
+
sponsorWalletRef: assertOptionalId(sponsorWalletRef, "sponsorWalletRef", { max: 200 }) ?? (() => {
|
|
126
|
+
throw new TypeError("sponsorWalletRef is required");
|
|
127
|
+
})(),
|
|
128
|
+
policyRef: assertOptionalId(policyRef, "policyRef", { max: 200 }) ?? (() => {
|
|
129
|
+
throw new TypeError("policyRef is required");
|
|
130
|
+
})(),
|
|
131
|
+
policyVersion: assertPositiveSafeInt(policyVersion, "policyVersion"),
|
|
132
|
+
policyFingerprint: assertOptionalSha256Hex(policyFingerprint, "policyFingerprint") ?? (() => {
|
|
133
|
+
throw new TypeError("policyFingerprint is required");
|
|
134
|
+
})(),
|
|
135
|
+
amountCents: assertPositiveSafeInt(amountCents, "amountCents"),
|
|
136
|
+
currency: normalizeCurrency(currency, "currency"),
|
|
137
|
+
payeeProviderId: assertOptionalId(payeeProviderId, "payeeProviderId", { max: 200 }) ?? (() => {
|
|
138
|
+
throw new TypeError("payeeProviderId is required");
|
|
139
|
+
})(),
|
|
140
|
+
...(assertOptionalId(quoteId, "quoteId", { max: 200 }) ? { quoteId: assertOptionalId(quoteId, "quoteId", { max: 200 }) } : {}),
|
|
141
|
+
...(assertOptionalSha256Hex(quoteSha256, "quoteSha256") ? { quoteSha256: assertOptionalSha256Hex(quoteSha256, "quoteSha256") } : {}),
|
|
142
|
+
...(normalizedRequestBindingMode ? { requestBindingMode: normalizedRequestBindingMode } : {}),
|
|
143
|
+
...(normalizedRequestBindingSha256 ? { requestBindingSha256: normalizedRequestBindingSha256 } : {}),
|
|
144
|
+
idempotencyKey: assertOptionalId(idempotencyKey, "idempotencyKey", { max: 256 }) ?? (() => {
|
|
145
|
+
throw new TypeError("idempotencyKey is required");
|
|
146
|
+
})(),
|
|
147
|
+
nonce: assertOptionalId(nonce, "nonce", { max: 256 }) ?? (() => {
|
|
148
|
+
throw new TypeError("nonce is required");
|
|
149
|
+
})(),
|
|
150
|
+
iat: normalizedIat,
|
|
151
|
+
exp: normalizedExp
|
|
152
|
+
},
|
|
153
|
+
{ path: "$" }
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function computeX402EscalationOverridePayloadHashV1(payload) {
|
|
158
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new TypeError("payload must be an object");
|
|
159
|
+
return sha256Hex(canonicalJsonStringify(payload));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function mintX402EscalationOverrideTokenV1({ payload, keyId = null, publicKeyPem = null, privateKeyPem } = {}) {
|
|
163
|
+
const normalizedPayload = buildX402EscalationOverridePayloadV1(payload ?? {});
|
|
164
|
+
const privatePem = assertNonEmptyString(privateKeyPem, "privateKeyPem");
|
|
165
|
+
const publicPem = publicKeyPem === null || publicKeyPem === undefined ? null : assertNonEmptyString(publicKeyPem, "publicKeyPem");
|
|
166
|
+
const derivedKeyId = publicPem ? keyIdFromPublicKeyPem(publicPem) : null;
|
|
167
|
+
const normalizedKeyId = keyId === null || keyId === undefined || String(keyId).trim() === "" ? derivedKeyId : String(keyId).trim();
|
|
168
|
+
if (!normalizedKeyId) throw new TypeError("keyId is required (or provide publicKeyPem)");
|
|
169
|
+
if (derivedKeyId && normalizedKeyId !== derivedKeyId) throw new TypeError("keyId does not match publicKeyPem");
|
|
170
|
+
|
|
171
|
+
const payloadHashHex = computeX402EscalationOverridePayloadHashV1(normalizedPayload);
|
|
172
|
+
const signatureBase64 = signHashHexEd25519(payloadHashHex, privatePem);
|
|
173
|
+
const envelope = normalizeForCanonicalJson(
|
|
174
|
+
{
|
|
175
|
+
v: X402_ESCALATION_OVERRIDE_TOKEN_VERSION,
|
|
176
|
+
kid: normalizedKeyId,
|
|
177
|
+
payload: normalizedPayload,
|
|
178
|
+
sig: Buffer.from(signatureBase64, "base64").toString("base64url")
|
|
179
|
+
},
|
|
180
|
+
{ path: "$" }
|
|
181
|
+
);
|
|
182
|
+
const token = Buffer.from(canonicalJsonStringify(envelope), "utf8").toString("base64url");
|
|
183
|
+
return {
|
|
184
|
+
token,
|
|
185
|
+
envelope,
|
|
186
|
+
kid: normalizedKeyId,
|
|
187
|
+
payload: normalizedPayload,
|
|
188
|
+
payloadHashHex,
|
|
189
|
+
tokenSha256: sha256Hex(token)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function verifyX402EscalationOverrideTokenV1({
|
|
194
|
+
token,
|
|
195
|
+
publicKeyPem,
|
|
196
|
+
nowUnixSeconds = Math.floor(Date.now() / 1000),
|
|
197
|
+
expected = {}
|
|
198
|
+
} = {}) {
|
|
199
|
+
try {
|
|
200
|
+
const { kid, payload, sig } = decodeEnvelope(token);
|
|
201
|
+
const normalizedPayload = buildX402EscalationOverridePayloadV1(payload);
|
|
202
|
+
const expectedKeyId = keyIdFromPublicKeyPem(assertNonEmptyString(publicKeyPem, "publicKeyPem"));
|
|
203
|
+
if (kid !== expectedKeyId) return { ok: false, code: "X402_ESCALATION_OVERRIDE_KEY_MISMATCH", kid };
|
|
204
|
+
const payloadHashHex = computeX402EscalationOverridePayloadHashV1(normalizedPayload);
|
|
205
|
+
const signatureBase64 = Buffer.from(sig, "base64url").toString("base64");
|
|
206
|
+
if (!verifyHashHexEd25519({ hashHex: payloadHashHex, signatureBase64, publicKeyPem })) {
|
|
207
|
+
return { ok: false, code: "X402_ESCALATION_OVERRIDE_SIGNATURE_INVALID", kid };
|
|
208
|
+
}
|
|
209
|
+
if (!Number.isSafeInteger(nowUnixSeconds) || nowUnixSeconds <= 0) {
|
|
210
|
+
throw new TypeError("nowUnixSeconds must be a positive safe integer");
|
|
211
|
+
}
|
|
212
|
+
if (normalizedPayload.exp <= nowUnixSeconds) {
|
|
213
|
+
return { ok: false, code: "X402_ESCALATION_OVERRIDE_EXPIRED", kid };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const expectedFields = [
|
|
217
|
+
"overrideId",
|
|
218
|
+
"escalationId",
|
|
219
|
+
"gateId",
|
|
220
|
+
"sponsorRef",
|
|
221
|
+
"sponsorWalletRef",
|
|
222
|
+
"policyRef",
|
|
223
|
+
"policyVersion",
|
|
224
|
+
"policyFingerprint",
|
|
225
|
+
"amountCents",
|
|
226
|
+
"currency",
|
|
227
|
+
"payeeProviderId",
|
|
228
|
+
"quoteId",
|
|
229
|
+
"quoteSha256",
|
|
230
|
+
"requestBindingMode",
|
|
231
|
+
"requestBindingSha256"
|
|
232
|
+
];
|
|
233
|
+
for (const field of expectedFields) {
|
|
234
|
+
if (!Object.prototype.hasOwnProperty.call(expected, field)) continue;
|
|
235
|
+
const expectedValueRaw = expected[field];
|
|
236
|
+
const expectedValue = expectedValueRaw === null ? null : String(expectedValueRaw);
|
|
237
|
+
const actualValue = normalizedPayload[field] === undefined || normalizedPayload[field] === null ? null : String(normalizedPayload[field]);
|
|
238
|
+
if (expectedValue !== actualValue) {
|
|
239
|
+
return { ok: false, code: "X402_ESCALATION_OVERRIDE_MISMATCH", field, expected: expectedValue, actual: actualValue, kid };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
ok: true,
|
|
245
|
+
code: null,
|
|
246
|
+
kid,
|
|
247
|
+
payload: normalizedPayload,
|
|
248
|
+
payloadHashHex,
|
|
249
|
+
tokenSha256: sha256Hex(assertNonEmptyString(token, "token"))
|
|
250
|
+
};
|
|
251
|
+
} catch (err) {
|
|
252
|
+
return {
|
|
253
|
+
ok: false,
|
|
254
|
+
code: "X402_ESCALATION_OVERRIDE_SCHEMA_INVALID",
|
|
255
|
+
error: err?.message ?? String(err ?? "")
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { normalizeForCanonicalJson } from "./canonical-json.js";
|
|
2
|
+
|
|
3
|
+
function assertPlainObject(value, name) {
|
|
4
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
|
|
5
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
6
|
+
throw new TypeError(`${name} must be a plain object`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function assertNonEmptyString(value, name) {
|
|
11
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
|
|
12
|
+
return String(value).trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeCurrency(value, name) {
|
|
16
|
+
const raw = typeof value === "string" && value.trim() !== "" ? value : "USD";
|
|
17
|
+
const out = raw.trim().toUpperCase();
|
|
18
|
+
if (!/^[A-Z][A-Z0-9_]{2,11}$/.test(out)) throw new TypeError(`${name} must match ^[A-Z][A-Z0-9_]{2,11}$`);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeNonNegativeSafeInt(value, name) {
|
|
23
|
+
const n = Number(value);
|
|
24
|
+
if (!Number.isSafeInteger(n) || n < 0) throw new TypeError(`${name} must be a non-negative safe integer`);
|
|
25
|
+
return n;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizePositiveSafeInt(value, name) {
|
|
29
|
+
const n = Number(value);
|
|
30
|
+
if (!Number.isSafeInteger(n) || n <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
31
|
+
return n;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseKeyValueHeader(text) {
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const part of String(text ?? "")
|
|
37
|
+
.split(";")
|
|
38
|
+
.map((s) => s.trim())
|
|
39
|
+
.filter(Boolean)) {
|
|
40
|
+
const idx = part.indexOf("=");
|
|
41
|
+
if (idx < 0) continue;
|
|
42
|
+
const k = part.slice(0, idx).trim();
|
|
43
|
+
const v = part.slice(idx + 1).trim();
|
|
44
|
+
if (!k) continue;
|
|
45
|
+
out[k] = v;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildX402SettlementTerms({
|
|
51
|
+
amountCents,
|
|
52
|
+
currency = "USD",
|
|
53
|
+
disputeWindowDays = 0,
|
|
54
|
+
disputeWindowMs = null,
|
|
55
|
+
holdbackBps = 0,
|
|
56
|
+
evidenceRequirements = null,
|
|
57
|
+
slaPolicy = null
|
|
58
|
+
} = {}) {
|
|
59
|
+
const normalizedDisputeWindowMs =
|
|
60
|
+
disputeWindowMs === null || disputeWindowMs === undefined ? null : normalizeNonNegativeSafeInt(disputeWindowMs, "disputeWindowMs");
|
|
61
|
+
const normalizedDisputeWindowDays =
|
|
62
|
+
normalizedDisputeWindowMs !== null ? Math.ceil(normalizedDisputeWindowMs / 86_400_000) : normalizeNonNegativeSafeInt(disputeWindowDays, "disputeWindowDays");
|
|
63
|
+
const normalizedHoldbackBps = normalizeNonNegativeSafeInt(holdbackBps, "holdbackBps");
|
|
64
|
+
if (normalizedHoldbackBps > 10_000) throw new TypeError("holdbackBps must be within 0..10000");
|
|
65
|
+
const terms = {
|
|
66
|
+
amountCents: normalizePositiveSafeInt(amountCents, "amountCents"),
|
|
67
|
+
currency: normalizeCurrency(currency, "currency"),
|
|
68
|
+
disputeWindowDays: normalizedDisputeWindowDays,
|
|
69
|
+
disputeWindowMs: normalizedDisputeWindowMs,
|
|
70
|
+
holdbackBps: normalizedHoldbackBps,
|
|
71
|
+
evidenceRequirements: evidenceRequirements && typeof evidenceRequirements === "object" ? evidenceRequirements : null,
|
|
72
|
+
slaPolicy: slaPolicy && typeof slaPolicy === "object" ? slaPolicy : null
|
|
73
|
+
};
|
|
74
|
+
return normalizeForCanonicalJson(terms, { path: "$" });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Best-effort parsing of upstream x402-style 402 metadata.
|
|
78
|
+
// This does not attempt to be the x402 spec; it normalizes common "key=value; ..." or JSON payloads.
|
|
79
|
+
export function parseX402PaymentRequired(response402Headers) {
|
|
80
|
+
if (response402Headers === null || response402Headers === undefined) return { ok: false, error: "missing headers" };
|
|
81
|
+
|
|
82
|
+
const headerValue = (() => {
|
|
83
|
+
if (typeof response402Headers === "string") return response402Headers;
|
|
84
|
+
if (response402Headers && typeof response402Headers === "object") {
|
|
85
|
+
const raw =
|
|
86
|
+
response402Headers["x-payment-required"] ??
|
|
87
|
+
response402Headers["X-Payment-Required"] ??
|
|
88
|
+
response402Headers["payment-required"] ??
|
|
89
|
+
response402Headers["Payment-Required"] ??
|
|
90
|
+
null;
|
|
91
|
+
return raw === null || raw === undefined ? "" : String(Array.isArray(raw) ? raw[0] : raw);
|
|
92
|
+
}
|
|
93
|
+
return "";
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
const text = String(headerValue ?? "").trim();
|
|
97
|
+
if (!text) return { ok: false, error: "missing x-payment-required" };
|
|
98
|
+
|
|
99
|
+
// JSON payload case.
|
|
100
|
+
if (text.startsWith("{") && text.endsWith("}")) {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(text);
|
|
103
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { ok: false, error: "invalid json header" };
|
|
104
|
+
return { ok: true, raw: text, fields: normalizeForCanonicalJson(parsed, { path: "$" }) };
|
|
105
|
+
} catch (err) {
|
|
106
|
+
return { ok: false, error: "invalid json header", message: err?.message ?? String(err ?? "") };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const fields = parseKeyValueHeader(text);
|
|
111
|
+
return { ok: true, raw: text, fields: normalizeForCanonicalJson(fields, { path: "$" }) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Placeholder: the "gate" middleware wiring is environment-specific (proxy framework, upstream fetch, etc.).
|
|
115
|
+
// Keep it as an explicit adapter surface rather than baking a single HTTP stack into the kernel.
|
|
116
|
+
export function createX402GateMiddleware() {
|
|
117
|
+
throw new Error("createX402GateMiddleware is not implemented in this repo (use API endpoints or build an adapter)");
|
|
118
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { canonicalJsonStringify, normalizeForCanonicalJson } from "./canonical-json.js";
|
|
2
|
+
import { keyIdFromPublicKeyPem, sha256Hex, signHashHexEd25519, verifyHashHexEd25519 } from "./crypto.js";
|
|
3
|
+
|
|
4
|
+
export const X402_PROVIDER_REFUND_DECISION_PAYLOAD_SCHEMA_VERSION = "X402ProviderRefundDecisionPayload.v1";
|
|
5
|
+
export const X402_PROVIDER_REFUND_DECISION_SCHEMA_VERSION = "X402ProviderRefundDecision.v1";
|
|
6
|
+
export const X402_PROVIDER_REFUND_DECISION_SIGNATURE_SCHEMA_VERSION = "X402ProviderRefundDecisionSignature.v1";
|
|
7
|
+
|
|
8
|
+
function assertNonEmptyString(value, name) {
|
|
9
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
|
|
10
|
+
return value.trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function assertPemString(value, name) {
|
|
14
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} must be a non-empty PEM string`);
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assertOptionalString(value, name, { max = 1000 } = {}) {
|
|
19
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
20
|
+
const out = String(value).trim();
|
|
21
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function assertOptionalId(value, name, { max = 200 } = {}) {
|
|
26
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
27
|
+
const out = String(value).trim();
|
|
28
|
+
if (out.length > max) throw new TypeError(`${name} must be <= ${max} chars`);
|
|
29
|
+
if (!/^[A-Za-z0-9:._/-]+$/.test(out)) throw new TypeError(`${name} must match ^[A-Za-z0-9:._/-]+$`);
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function assertIsoDateTime(value, name) {
|
|
34
|
+
const out = assertNonEmptyString(value, name);
|
|
35
|
+
if (!Number.isFinite(Date.parse(out))) throw new TypeError(`${name} must be an ISO date-time`);
|
|
36
|
+
return new Date(out).toISOString();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function assertSha256Hex(value, name) {
|
|
40
|
+
const out = assertNonEmptyString(value, name).toLowerCase();
|
|
41
|
+
if (!/^[0-9a-f]{64}$/.test(out)) throw new TypeError(`${name} must be sha256 hex`);
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assertOptionalSha256Hex(value, name) {
|
|
46
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
47
|
+
return assertSha256Hex(value, name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function assertDecision(value, name = "decision") {
|
|
51
|
+
const out = assertNonEmptyString(value, name).toLowerCase();
|
|
52
|
+
if (out !== "accepted" && out !== "denied") throw new TypeError(`${name} must be accepted|denied`);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildX402ProviderRefundDecisionPayloadV1({
|
|
57
|
+
decisionId = null,
|
|
58
|
+
receiptId,
|
|
59
|
+
gateId,
|
|
60
|
+
quoteId = null,
|
|
61
|
+
requestSha256 = null,
|
|
62
|
+
decision,
|
|
63
|
+
reason = null,
|
|
64
|
+
decidedAt
|
|
65
|
+
} = {}) {
|
|
66
|
+
return normalizeForCanonicalJson(
|
|
67
|
+
{
|
|
68
|
+
schemaVersion: X402_PROVIDER_REFUND_DECISION_PAYLOAD_SCHEMA_VERSION,
|
|
69
|
+
...(assertOptionalId(decisionId, "decisionId", { max: 200 }) ? { decisionId: assertOptionalId(decisionId, "decisionId", { max: 200 }) } : {}),
|
|
70
|
+
receiptId: assertOptionalId(receiptId, "receiptId", { max: 200 }) ?? (() => {
|
|
71
|
+
throw new TypeError("receiptId is required");
|
|
72
|
+
})(),
|
|
73
|
+
gateId: assertOptionalId(gateId, "gateId", { max: 200 }) ?? (() => {
|
|
74
|
+
throw new TypeError("gateId is required");
|
|
75
|
+
})(),
|
|
76
|
+
...(assertOptionalId(quoteId, "quoteId", { max: 200 }) ? { quoteId: assertOptionalId(quoteId, "quoteId", { max: 200 }) } : {}),
|
|
77
|
+
...(assertOptionalSha256Hex(requestSha256, "requestSha256") ? { requestSha256: assertOptionalSha256Hex(requestSha256, "requestSha256") } : {}),
|
|
78
|
+
decision: assertDecision(decision, "decision"),
|
|
79
|
+
...(assertOptionalString(reason, "reason", { max: 1000 }) ? { reason: assertOptionalString(reason, "reason", { max: 1000 }) } : {}),
|
|
80
|
+
decidedAt: assertIsoDateTime(decidedAt, "decidedAt")
|
|
81
|
+
},
|
|
82
|
+
{ path: "$" }
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function computeX402ProviderRefundDecisionPayloadHashV1({ payload } = {}) {
|
|
87
|
+
const normalizedPayload = buildX402ProviderRefundDecisionPayloadV1(payload ?? {});
|
|
88
|
+
return sha256Hex(canonicalJsonStringify(normalizedPayload));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function normalizeDecisionEnvelope(decision) {
|
|
92
|
+
if (!decision || typeof decision !== "object" || Array.isArray(decision)) throw new TypeError("decision must be an object");
|
|
93
|
+
const payload = buildX402ProviderRefundDecisionPayloadV1(decision);
|
|
94
|
+
const signature = decision.signature;
|
|
95
|
+
if (!signature || typeof signature !== "object" || Array.isArray(signature)) throw new TypeError("decision.signature must be an object");
|
|
96
|
+
if (String(signature.schemaVersion ?? "") !== X402_PROVIDER_REFUND_DECISION_SIGNATURE_SCHEMA_VERSION) {
|
|
97
|
+
throw new TypeError(`decision.signature.schemaVersion must be ${X402_PROVIDER_REFUND_DECISION_SIGNATURE_SCHEMA_VERSION}`);
|
|
98
|
+
}
|
|
99
|
+
if (String(signature.algorithm ?? "").toLowerCase() !== "ed25519") throw new TypeError("decision.signature.algorithm must be ed25519");
|
|
100
|
+
return {
|
|
101
|
+
schemaVersion: decision.schemaVersion ?? X402_PROVIDER_REFUND_DECISION_SCHEMA_VERSION,
|
|
102
|
+
payload,
|
|
103
|
+
signature: normalizeForCanonicalJson(
|
|
104
|
+
{
|
|
105
|
+
schemaVersion: X402_PROVIDER_REFUND_DECISION_SIGNATURE_SCHEMA_VERSION,
|
|
106
|
+
algorithm: "ed25519",
|
|
107
|
+
keyId: assertNonEmptyString(signature.keyId, "decision.signature.keyId"),
|
|
108
|
+
signedAt: assertIsoDateTime(signature.signedAt, "decision.signature.signedAt"),
|
|
109
|
+
payloadHash: assertSha256Hex(signature.payloadHash, "decision.signature.payloadHash"),
|
|
110
|
+
signatureBase64: assertNonEmptyString(signature.signatureBase64, "decision.signature.signatureBase64")
|
|
111
|
+
},
|
|
112
|
+
{ path: "$" }
|
|
113
|
+
)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function signX402ProviderRefundDecisionV1({ decision, signedAt, publicKeyPem, privateKeyPem } = {}) {
|
|
118
|
+
const payload = buildX402ProviderRefundDecisionPayloadV1(decision ?? {});
|
|
119
|
+
const signerPublicKeyPem = assertPemString(publicKeyPem, "publicKeyPem");
|
|
120
|
+
const signerPrivateKeyPem = assertPemString(privateKeyPem, "privateKeyPem");
|
|
121
|
+
const payloadHash = computeX402ProviderRefundDecisionPayloadHashV1({ payload });
|
|
122
|
+
const signatureBase64 = signHashHexEd25519(payloadHash, signerPrivateKeyPem);
|
|
123
|
+
const payloadFields = { ...payload };
|
|
124
|
+
delete payloadFields.schemaVersion;
|
|
125
|
+
return normalizeForCanonicalJson(
|
|
126
|
+
{
|
|
127
|
+
schemaVersion: X402_PROVIDER_REFUND_DECISION_SCHEMA_VERSION,
|
|
128
|
+
...payloadFields,
|
|
129
|
+
signature: {
|
|
130
|
+
schemaVersion: X402_PROVIDER_REFUND_DECISION_SIGNATURE_SCHEMA_VERSION,
|
|
131
|
+
algorithm: "ed25519",
|
|
132
|
+
keyId: keyIdFromPublicKeyPem(signerPublicKeyPem),
|
|
133
|
+
signedAt: assertIsoDateTime(signedAt, "signedAt"),
|
|
134
|
+
payloadHash,
|
|
135
|
+
signatureBase64
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{ path: "$" }
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function verifyX402ProviderRefundDecisionV1({
|
|
143
|
+
decision,
|
|
144
|
+
publicKeyPem,
|
|
145
|
+
expectedReceiptId = null,
|
|
146
|
+
expectedGateId = null,
|
|
147
|
+
expectedQuoteId = null,
|
|
148
|
+
expectedRequestSha256 = null,
|
|
149
|
+
expectedDecision = null
|
|
150
|
+
} = {}) {
|
|
151
|
+
try {
|
|
152
|
+
const signerPublicKeyPem = assertPemString(publicKeyPem, "publicKeyPem");
|
|
153
|
+
const normalized = normalizeDecisionEnvelope(decision);
|
|
154
|
+
if (String(normalized.schemaVersion ?? "") !== X402_PROVIDER_REFUND_DECISION_SCHEMA_VERSION) {
|
|
155
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_SCHEMA_INVALID", error: "invalid schemaVersion" };
|
|
156
|
+
}
|
|
157
|
+
const expectedKeyId = keyIdFromPublicKeyPem(signerPublicKeyPem);
|
|
158
|
+
if (normalized.signature.keyId !== expectedKeyId) {
|
|
159
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_KEY_ID_MISMATCH", error: "signature keyId mismatch" };
|
|
160
|
+
}
|
|
161
|
+
const payloadHash = computeX402ProviderRefundDecisionPayloadHashV1({ payload: normalized.payload });
|
|
162
|
+
if (normalized.signature.payloadHash !== payloadHash) {
|
|
163
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_PAYLOAD_HASH_MISMATCH", error: "payload hash mismatch" };
|
|
164
|
+
}
|
|
165
|
+
const signatureValid = verifyHashHexEd25519({
|
|
166
|
+
hashHex: payloadHash,
|
|
167
|
+
signatureBase64: normalized.signature.signatureBase64,
|
|
168
|
+
publicKeyPem: signerPublicKeyPem
|
|
169
|
+
});
|
|
170
|
+
if (!signatureValid) {
|
|
171
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_SIGNATURE_INVALID", error: "signature invalid" };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (expectedReceiptId !== null) {
|
|
175
|
+
const expected = assertOptionalId(expectedReceiptId, "expectedReceiptId", { max: 200 });
|
|
176
|
+
if (expected !== normalized.payload.receiptId) {
|
|
177
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_RECEIPT_MISMATCH", error: "receiptId mismatch" };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (expectedGateId !== null) {
|
|
181
|
+
const expected = assertOptionalId(expectedGateId, "expectedGateId", { max: 200 });
|
|
182
|
+
if (expected !== normalized.payload.gateId) {
|
|
183
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_GATE_MISMATCH", error: "gateId mismatch" };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (expectedQuoteId !== null) {
|
|
187
|
+
const expected = assertOptionalId(expectedQuoteId, "expectedQuoteId", { max: 200 });
|
|
188
|
+
if (expected !== normalized.payload.quoteId) {
|
|
189
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_QUOTE_MISMATCH", error: "quoteId mismatch" };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (expectedRequestSha256 !== null) {
|
|
193
|
+
const expected = assertOptionalSha256Hex(expectedRequestSha256, "expectedRequestSha256");
|
|
194
|
+
if (expected !== normalized.payload.requestSha256) {
|
|
195
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_REQUEST_HASH_MISMATCH", error: "requestSha256 mismatch" };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (expectedDecision !== null) {
|
|
199
|
+
const expected = assertDecision(expectedDecision, "expectedDecision");
|
|
200
|
+
if (expected !== normalized.payload.decision) {
|
|
201
|
+
return { ok: false, code: "X402_PROVIDER_REFUND_DECISION_VALUE_MISMATCH", error: "decision mismatch" };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
ok: true,
|
|
207
|
+
code: null,
|
|
208
|
+
error: null,
|
|
209
|
+
payload: normalized.payload,
|
|
210
|
+
payloadHash,
|
|
211
|
+
keyId: normalized.signature.keyId
|
|
212
|
+
};
|
|
213
|
+
} catch (err) {
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
code: "X402_PROVIDER_REFUND_DECISION_SCHEMA_INVALID",
|
|
217
|
+
error: err?.message ?? String(err ?? "")
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|