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.
Files changed (483) hide show
  1. package/README.md +93 -3
  2. package/SETTLD_VERSION +1 -1
  3. package/bin/settld-mcp +2 -0
  4. package/bin/settld.js +71 -0
  5. package/conformance/kernel-v0/README.md +7 -0
  6. package/conformance/kernel-v0/run.mjs +292 -4
  7. package/docs/ACCESS.md +57 -0
  8. package/docs/ADOPTION_CHECKLIST.md +44 -0
  9. package/docs/ALERTS.md +198 -0
  10. package/docs/ARCHITECTURE.md +69 -0
  11. package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
  12. package/docs/ARTIFACTS.md +60 -0
  13. package/docs/CERTIFICATION_CHECKLIST.md +33 -0
  14. package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
  15. package/docs/CONFIG.md +297 -0
  16. package/docs/CONTRACTS_APIS.md +23 -0
  17. package/docs/DEPRECATION.md +31 -0
  18. package/docs/DOMAIN_MODEL.md +92 -0
  19. package/docs/EVENT_ENVELOPE.md +53 -0
  20. package/docs/FINANCE_PACK_FORMAT.md +53 -0
  21. package/docs/INCIDENT_TAXONOMY.md +30 -0
  22. package/docs/JOB_STATE_MACHINE.md +66 -0
  23. package/docs/KERNEL_COMPATIBLE.md +60 -0
  24. package/docs/KERNEL_V0.md +40 -0
  25. package/docs/KEY_ROTATION.md +80 -0
  26. package/docs/LEDGER.md +82 -0
  27. package/docs/LIVENESS.md +76 -0
  28. package/docs/MVP_BUILD_ORDER.md +36 -0
  29. package/docs/ONCALL_PLAYBOOK.md +39 -0
  30. package/docs/OPERATIONS_SIGNING.md +20 -0
  31. package/docs/OVERVIEW.md +190 -0
  32. package/docs/PERF_BASELINE.md +85 -0
  33. package/docs/PRD.md +77 -0
  34. package/docs/QUICKSTART_KERNEL_V0.md +96 -0
  35. package/docs/QUICKSTART_MCP.md +377 -0
  36. package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
  37. package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
  38. package/docs/QUICKSTART_PRODUCE.md +61 -0
  39. package/docs/QUICKSTART_PROFILES.md +198 -0
  40. package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
  41. package/docs/QUICKSTART_SDK.md +125 -0
  42. package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
  43. package/docs/QUICKSTART_VERIFY.md +54 -0
  44. package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
  45. package/docs/README.md +33 -0
  46. package/docs/RELEASE_CHECKLIST.md +182 -0
  47. package/docs/RELEASING.md +82 -0
  48. package/docs/REPO_SETTINGS.md +37 -0
  49. package/docs/RUNBOOK.md +86 -0
  50. package/docs/SKILLS.md +42 -0
  51. package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
  52. package/docs/SLO.md +131 -0
  53. package/docs/SUMMARY.md +17 -0
  54. package/docs/SUPPORT.md +31 -0
  55. package/docs/THREAT_MODEL.md +36 -0
  56. package/docs/TRUST.md +59 -0
  57. package/docs/WORKFLOW.md +35 -0
  58. package/docs/X402_BATCH_SETTLEMENT.md +126 -0
  59. package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
  60. package/docs/examples/x402-provider-payout-registry.example.json +14 -0
  61. package/docs/gitbook/README.md +64 -0
  62. package/docs/gitbook/SETUP.md +25 -0
  63. package/docs/gitbook/SUMMARY.md +15 -0
  64. package/docs/gitbook/api-reference.md +73 -0
  65. package/docs/gitbook/closepacks.md +55 -0
  66. package/docs/gitbook/conformance.md +59 -0
  67. package/docs/gitbook/core-primitives.md +85 -0
  68. package/docs/gitbook/dispute-lifecycle.md +33 -0
  69. package/docs/gitbook/faq.md +21 -0
  70. package/docs/gitbook/guides.md +49 -0
  71. package/docs/gitbook/operations-runbook.md +36 -0
  72. package/docs/gitbook/quickstart.md +103 -0
  73. package/docs/gitbook/replay-and-audit.md +30 -0
  74. package/docs/gitbook/sdk-reference.md +35 -0
  75. package/docs/gitbook/security-model.md +58 -0
  76. package/docs/integrations/README.md +15 -0
  77. package/docs/integrations/github-actions-verify.yml +31 -0
  78. package/docs/integrations/github-actions.md +34 -0
  79. package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
  80. package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
  81. package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
  82. package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
  83. package/docs/kernel-compatible/capabilities.json +36 -0
  84. package/docs/marketing/agent-commerce-substrate.md +78 -0
  85. package/docs/marketing/hn-repost-2026-02-17.md +102 -0
  86. package/docs/marketing/show-hn-post.md +45 -0
  87. package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
  88. package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
  89. package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
  90. package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
  91. package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
  92. package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
  93. package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
  94. package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
  95. package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
  96. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
  97. package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
  98. package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
  99. package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
  100. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
  101. package/docs/ops/R1_SLOS.md +66 -0
  102. package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
  103. package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
  104. package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
  105. package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
  106. package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
  107. package/docs/ops/tool-call-disputes-holdback.md +52 -0
  108. package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
  109. package/docs/pilot-kit/README.md +29 -0
  110. package/docs/pilot-kit/architecture-one-pager.md +48 -0
  111. package/docs/pilot-kit/buyer-email.txt +19 -0
  112. package/docs/pilot-kit/buyer-one-pager.md +31 -0
  113. package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
  114. package/docs/pilot-kit/offline-verify.md +33 -0
  115. package/docs/pilot-kit/procurement-one-pager.md +50 -0
  116. package/docs/pilot-kit/rfp-clause.md +46 -0
  117. package/docs/pilot-kit/roi-calculator-template.csv +2 -0
  118. package/docs/pilot-kit/security-qa.md +153 -0
  119. package/docs/pilot-kit/security-summary.md +35 -0
  120. package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
  121. package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
  122. package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
  123. package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
  124. package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
  125. package/docs/spec/AcceptanceCriteria.v1.md +17 -0
  126. package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
  127. package/docs/spec/AgentEvent.v1.md +47 -0
  128. package/docs/spec/AgentIdentity.v1.md +62 -0
  129. package/docs/spec/AgentPassport.v1.md +95 -0
  130. package/docs/spec/AgentReputation.v1.md +59 -0
  131. package/docs/spec/AgentReputation.v2.md +52 -0
  132. package/docs/spec/AgentRun.v1.md +47 -0
  133. package/docs/spec/AgentRunSettlement.v1.md +52 -0
  134. package/docs/spec/AgentWallet.v1.md +43 -0
  135. package/docs/spec/AgreementDelegation.v1.md +109 -0
  136. package/docs/spec/ArbitrationCase.v1.md +67 -0
  137. package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
  138. package/docs/spec/ArbitrationVerdict.v1.md +60 -0
  139. package/docs/spec/BundleHeadAttestation.v1.md +32 -0
  140. package/docs/spec/CANONICAL_JSON.md +31 -0
  141. package/docs/spec/CRYPTOGRAPHY.md +61 -0
  142. package/docs/spec/ClosePack.v1.md +49 -0
  143. package/docs/spec/ClosePackManifest.v1.md +24 -0
  144. package/docs/spec/DelegationGrant.v1.md +90 -0
  145. package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
  146. package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
  147. package/docs/spec/ERRORS.md +76 -0
  148. package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
  149. package/docs/spec/EvidenceIndex.v1.md +20 -0
  150. package/docs/spec/ExecutionIntent.v1.md +90 -0
  151. package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
  152. package/docs/spec/FundingHold.v1.md +60 -0
  153. package/docs/spec/GovernancePolicy.v1.md +34 -0
  154. package/docs/spec/GovernancePolicy.v2.md +30 -0
  155. package/docs/spec/INVARIANTS.md +389 -0
  156. package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
  157. package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
  158. package/docs/spec/InvoiceClaim.v1.md +11 -0
  159. package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
  160. package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
  161. package/docs/spec/MarketplaceOffer.v2.md +54 -0
  162. package/docs/spec/MeteringReport.v1.md +18 -0
  163. package/docs/spec/OperatorAction.v1.md +90 -0
  164. package/docs/spec/PRODUCER_ERRORS.md +42 -0
  165. package/docs/spec/PolicyDecision.v1.md +83 -0
  166. package/docs/spec/PricingMatrix.v1.md +20 -0
  167. package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
  168. package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
  169. package/docs/spec/ProduceCliOutput.v1.md +46 -0
  170. package/docs/spec/ProofBundleManifest.v1.md +24 -0
  171. package/docs/spec/README.md +109 -0
  172. package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
  173. package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
  174. package/docs/spec/REMOTE_SIGNER.md +66 -0
  175. package/docs/spec/ReleaseIndex.v1.md +32 -0
  176. package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
  177. package/docs/spec/ReleaseTrust.v1.md +13 -0
  178. package/docs/spec/ReleaseTrust.v2.md +26 -0
  179. package/docs/spec/RemoteSignerRequest.v1.md +21 -0
  180. package/docs/spec/RemoteSignerResponse.v1.md +16 -0
  181. package/docs/spec/ReputationEvent.v1.md +63 -0
  182. package/docs/spec/RevocationList.v1.md +28 -0
  183. package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
  184. package/docs/spec/STRICTNESS.md +68 -0
  185. package/docs/spec/SUPPLY_CHAIN.md +33 -0
  186. package/docs/spec/SettlementAdjustment.v1.md +45 -0
  187. package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
  188. package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
  189. package/docs/spec/SettlementDecisionReport.v1.md +44 -0
  190. package/docs/spec/SettlementKernel.v1.md +59 -0
  191. package/docs/spec/SettlementReceipt.v1.md +63 -0
  192. package/docs/spec/SlaDefinition.v1.md +24 -0
  193. package/docs/spec/SlaEvaluation.v1.md +12 -0
  194. package/docs/spec/THREAT_MODEL.md +113 -0
  195. package/docs/spec/TOOL_PROVENANCE.md +30 -0
  196. package/docs/spec/TRUST_ANCHORS.md +84 -0
  197. package/docs/spec/TenantSettings.v1.md +90 -0
  198. package/docs/spec/TenantSettings.v2.md +99 -0
  199. package/docs/spec/TimestampProof.v1.md +25 -0
  200. package/docs/spec/ToolCallAgreement.v1.md +34 -0
  201. package/docs/spec/ToolCallEvidence.v1.md +47 -0
  202. package/docs/spec/ToolManifest.v1.md +47 -0
  203. package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
  204. package/docs/spec/VERSIONING.md +107 -0
  205. package/docs/spec/VerificationReport.v1.md +50 -0
  206. package/docs/spec/VerifyAboutOutput.v1.md +10 -0
  207. package/docs/spec/VerifyCliOutput.v1.md +28 -0
  208. package/docs/spec/WARNINGS.md +83 -0
  209. package/docs/spec/error-codes.v1.txt +285 -0
  210. package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
  211. package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
  212. package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
  213. package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
  214. package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
  215. package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
  216. package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
  217. package/docs/spec/examples/release_index_v1.example.json +15 -0
  218. package/docs/spec/examples/release_trust_v1.example.json +7 -0
  219. package/docs/spec/examples/release_trust_v2.example.json +22 -0
  220. package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
  221. package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
  222. package/docs/spec/examples/reputation_event_v1.example.json +29 -0
  223. package/docs/spec/examples/verification_report_v1.example.json +24 -0
  224. package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
  225. package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
  226. package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
  227. package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
  228. package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
  229. package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
  230. package/docs/spec/producer-error-codes.v1.txt +14 -0
  231. package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
  232. package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
  233. package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
  234. package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
  235. package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
  236. package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
  237. package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
  238. package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
  239. package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
  240. package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
  241. package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
  242. package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
  243. package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
  244. package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
  245. package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
  246. package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
  247. package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
  248. package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
  249. package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
  250. package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
  251. package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
  252. package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
  253. package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
  254. package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
  255. package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
  256. package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
  257. package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
  258. package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
  259. package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
  260. package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
  261. package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
  262. package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
  263. package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
  264. package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
  265. package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
  266. package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
  267. package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
  268. package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
  269. package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
  270. package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
  271. package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
  272. package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
  273. package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
  274. package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
  275. package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
  276. package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
  277. package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
  278. package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
  279. package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
  280. package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
  281. package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
  282. package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
  283. package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
  284. package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
  285. package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
  286. package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
  287. package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
  288. package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
  289. package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
  290. package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
  291. package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
  292. package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
  293. package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
  294. package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
  295. package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
  296. package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
  297. package/docs/spec/x402-error-codes.v1.txt +35 -0
  298. package/docs/templates/buyer-email.txt +18 -0
  299. package/docs/templates/buyer-one-pager.md +24 -0
  300. package/package.json +53 -6
  301. package/scripts/acceptance/full-stack.mjs +734 -0
  302. package/scripts/acceptance/full-stack.sh +99 -0
  303. package/scripts/audit/build-audit-packet.mjs +242 -0
  304. package/scripts/backup-pg.sh +45 -0
  305. package/scripts/backup-restore/README.md +18 -0
  306. package/scripts/backup-restore/capture-state.mjs +130 -0
  307. package/scripts/backup-restore/client.mjs +97 -0
  308. package/scripts/backup-restore/seed-workload.mjs +235 -0
  309. package/scripts/backup-restore/verify-state.mjs +139 -0
  310. package/scripts/backup-restore-test.sh +217 -0
  311. package/scripts/chaos.js +221 -0
  312. package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
  313. package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
  314. package/scripts/ci/changelog-guard.mjs +145 -0
  315. package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
  316. package/scripts/ci/check-secret-hygiene.mjs +78 -0
  317. package/scripts/ci/check-version-consistency.mjs +42 -0
  318. package/scripts/ci/cli-pack-smoke.mjs +160 -0
  319. package/scripts/ci/flake-budget-guard.mjs +68 -0
  320. package/scripts/ci/generate-error-codes.mjs +54 -0
  321. package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
  322. package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
  323. package/scripts/ci/npm-pack-smoke.mjs +454 -0
  324. package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
  325. package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
  326. package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
  327. package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
  328. package/scripts/ci/run-go-live-gate.mjs +150 -0
  329. package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
  330. package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
  331. package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
  332. package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
  333. package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
  334. package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
  335. package/scripts/ci/run-production-cutover-gate.mjs +540 -0
  336. package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
  337. package/scripts/ci/run-release-promotion-guard.mjs +756 -0
  338. package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
  339. package/scripts/ci/runtime-import-smoke.mjs +58 -0
  340. package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
  341. package/scripts/closepack/lib.mjs +286 -0
  342. package/scripts/collect-debug.sh +263 -0
  343. package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
  344. package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
  345. package/scripts/demo/delivery-robot/generate.mjs +377 -0
  346. package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
  347. package/scripts/demo/magic-link-first-green.mjs +118 -0
  348. package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
  349. package/scripts/demo/mcp-paid-exa.mjs +1110 -0
  350. package/scripts/dev/billing-doctor.sh +145 -0
  351. package/scripts/dev/billing-smoke-prod.sh +219 -0
  352. package/scripts/dev/billing-webhook-replay.sh +161 -0
  353. package/scripts/dev/env.dev.example +29 -0
  354. package/scripts/dev/env.sh +37 -0
  355. package/scripts/dev/new-sdk-key.sh +81 -0
  356. package/scripts/dev/sdk-first-run.sh +21 -0
  357. package/scripts/dev/smoke-x402-gateway.sh +115 -0
  358. package/scripts/dev/start-api.sh +24 -0
  359. package/scripts/doctor/mcp-host.mjs +120 -0
  360. package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
  361. package/scripts/examples/sdk-first-paid-rfq.py +105 -0
  362. package/scripts/examples/sdk-first-verified-run.mjs +85 -0
  363. package/scripts/examples/sdk-first-verified-run.py +99 -0
  364. package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
  365. package/scripts/examples/sdk-tenant-analytics.py +118 -0
  366. package/scripts/finance-pack/bundle.mjs +284 -0
  367. package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
  368. package/scripts/governance/export.mjs +169 -0
  369. package/scripts/load/delivery-stress.k6.js +183 -0
  370. package/scripts/load/ingest-burst.k6.js +236 -0
  371. package/scripts/load/run-delivery-load.js +66 -0
  372. package/scripts/load/webhook-receiver.js +131 -0
  373. package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
  374. package/scripts/mcp/probe.mjs +238 -0
  375. package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
  376. package/scripts/mcp/settld-mcp-server.mjs +1511 -0
  377. package/scripts/openapi/write.mjs +13 -0
  378. package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
  379. package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
  380. package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
  381. package/scripts/ops/design-partner-run-packet.mjs +466 -0
  382. package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
  383. package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
  384. package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
  385. package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
  386. package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
  387. package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
  388. package/scripts/pilot/finance-pack.mjs +495 -0
  389. package/scripts/pilot/fixtures/robot-keypair.json +4 -0
  390. package/scripts/pilot/fixtures/server-signer.json +4 -0
  391. package/scripts/policy/cli.mjs +600 -0
  392. package/scripts/profile/cli.mjs +1324 -0
  393. package/scripts/proof-bundle/job.mjs +109 -0
  394. package/scripts/proof-bundle/lib.mjs +92 -0
  395. package/scripts/proof-bundle/month.mjs +103 -0
  396. package/scripts/provider/conformance-run.mjs +159 -0
  397. package/scripts/provider/keys-generate.mjs +135 -0
  398. package/scripts/provider/publish.mjs +420 -0
  399. package/scripts/quickstart/x402.mjs +334 -0
  400. package/scripts/register-entity-secret.mjs +102 -0
  401. package/scripts/release/build-artifacts.mjs +181 -0
  402. package/scripts/release/generate-release-index.mjs +112 -0
  403. package/scripts/release/release-index-lib.mjs +232 -0
  404. package/scripts/release/sign-release-index.mjs +85 -0
  405. package/scripts/release/validate-release-assets.mjs +170 -0
  406. package/scripts/release/verify-release.mjs +261 -0
  407. package/scripts/restore-pg.sh +34 -0
  408. package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
  409. package/scripts/sdk/smoke-python.py +30 -0
  410. package/scripts/sdk/smoke.mjs +16 -0
  411. package/scripts/settlement/x402-batch-worker.mjs +1091 -0
  412. package/scripts/setup/circle-bootstrap.mjs +310 -0
  413. package/scripts/setup/host-config.mjs +617 -0
  414. package/scripts/setup/onboard.mjs +1337 -0
  415. package/scripts/setup/openclaw-onboard.mjs +423 -0
  416. package/scripts/setup/wizard.mjs +986 -0
  417. package/scripts/slo/check.mjs +239 -0
  418. package/scripts/smoke/k8s-smoke.mjs +214 -0
  419. package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
  420. package/scripts/test/check-no-generated-artifacts.sh +12 -0
  421. package/scripts/test/run.sh +59 -0
  422. package/scripts/trust/validate-trust-file.mjs +57 -0
  423. package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
  424. package/scripts/trust-config/wizard.mjs +161 -0
  425. package/scripts/vendor-contract-test-lib.mjs +182 -0
  426. package/scripts/vendor-contract-test.mjs +55 -0
  427. package/scripts/vercel/build-mkdocs.sh +9 -0
  428. package/scripts/vercel/ignore-mkdocs.sh +25 -0
  429. package/scripts/vercel/install-mkdocs.sh +6 -0
  430. package/scripts/verify-pg.js +217 -0
  431. package/scripts/x402/receipt-verify.mjs +289 -0
  432. package/services/finance-sink/src/dedupe-store.js +29 -6
  433. package/services/receiver/src/dedupe-store.js +29 -5
  434. package/services/x402-gateway/Dockerfile +13 -0
  435. package/services/x402-gateway/README.md +58 -0
  436. package/services/x402-gateway/examples/upstream-mock.js +337 -0
  437. package/services/x402-gateway/src/server.js +1058 -0
  438. package/src/api/app.js +34658 -16940
  439. package/src/api/maintenance.js +70 -0
  440. package/src/api/middleware/trust-kernel.js +114 -0
  441. package/src/api/openapi.js +1778 -70
  442. package/src/api/persistence.js +456 -0
  443. package/src/api/server.js +81 -5
  444. package/src/api/store.js +1581 -62
  445. package/src/api/workers/deliveries.js +99 -4
  446. package/src/api/workers/insolvency-sweep.js +159 -0
  447. package/src/core/agent-card.js +69 -0
  448. package/src/core/agent-wallets.js +231 -0
  449. package/src/core/agreement-delegation.js +549 -0
  450. package/src/core/billing-plans.js +40 -6
  451. package/src/core/circle-reserve-adapter.js +845 -0
  452. package/src/core/event-policy.js +21 -2
  453. package/src/core/maintenance-locks.js +1 -0
  454. package/src/core/operator-action.js +303 -0
  455. package/src/core/paid-tool-manifest.js +318 -0
  456. package/src/core/policy-decision.js +322 -0
  457. package/src/core/policy-packs.js +207 -0
  458. package/src/core/profile-fingerprint.js +27 -0
  459. package/src/core/profile-simulation-reasons.js +84 -0
  460. package/src/core/profile-templates.js +242 -0
  461. package/src/core/provider-publish-conformance.js +525 -0
  462. package/src/core/provider-publish-proof.js +396 -0
  463. package/src/core/provider-quote-signature.js +170 -0
  464. package/src/core/settld-keys.js +112 -0
  465. package/src/core/settld-pay-token.js +344 -0
  466. package/src/core/settlement-kernel.js +239 -2
  467. package/src/core/settlement-verifier.js +335 -0
  468. package/src/core/tool-call-agreement.js +112 -0
  469. package/src/core/tool-call-evidence.js +144 -0
  470. package/src/core/tool-provider-signature.js +98 -0
  471. package/src/core/wallet-assignment-resolver.js +129 -0
  472. package/src/core/wallet-provider-bootstrap.js +365 -0
  473. package/src/core/x402-escalation-override.js +258 -0
  474. package/src/core/x402-gate.js +118 -0
  475. package/src/core/x402-provider-refund-decision.js +220 -0
  476. package/src/core/x402-receipt-verifier.js +708 -0
  477. package/src/core/x402-reversal-command.js +251 -0
  478. package/src/core/x402-wallet-issuer-decision.js +252 -0
  479. package/src/core/zk-verifier.js +300 -0
  480. package/src/db/migrations/029_reputation_event_index.sql +54 -0
  481. package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
  482. package/src/db/pg.js +18 -7
  483. package/src/db/store-pg.js +1508 -111
@@ -0,0 +1,1110 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "node:child_process";
4
+ import { mkdir, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { setTimeout as delay } from "node:timers/promises";
8
+
9
+ import { canonicalJsonStringify } from "../../src/core/canonical-json.js";
10
+ import { sha256Hex } from "../../src/core/crypto.js";
11
+ import { verifySettldPayTokenV1 } from "../../src/core/settld-pay-token.js";
12
+ import { computeToolProviderSignaturePayloadHashV1, verifyToolProviderSignatureV1 } from "../../src/core/tool-provider-signature.js";
13
+ import { verifyX402ReceiptRecord } from "../../src/core/x402-receipt-verifier.js";
14
+
15
+ function usage() {
16
+ return [
17
+ "Usage:",
18
+ " node scripts/demo/mcp-paid-exa.mjs [--circle <stub|sandbox|production>] [--workload <exa|weather|llm>]",
19
+ " node scripts/demo/mcp-paid-exa.mjs --circle=sandbox",
20
+ " node scripts/demo/mcp-paid-exa.mjs --workload=weather",
21
+ " node scripts/demo/mcp-paid-exa.mjs --workload=llm",
22
+ "",
23
+ "Environment overrides:",
24
+ " SETTLD_DEMO_CIRCLE_MODE=stub|sandbox|production",
25
+ " SETTLD_DEMO_WORKLOAD=exa|weather|llm",
26
+ " SETTLD_DEMO_RUN_BATCH_SETTLEMENT=1",
27
+ " SETTLD_DEMO_BATCH_PROVIDER_WALLET_ID=<circle wallet id>"
28
+ ].join("\n");
29
+ }
30
+
31
+ function parseCliArgs(argv) {
32
+ const out = {
33
+ circleMode: null,
34
+ workload: null,
35
+ help: false
36
+ };
37
+ for (let i = 0; i < argv.length; i += 1) {
38
+ const arg = String(argv[i] ?? "").trim();
39
+ if (!arg) continue;
40
+ if (arg === "--help" || arg === "-h") {
41
+ out.help = true;
42
+ continue;
43
+ }
44
+ if (arg === "--circle") {
45
+ const value = String(argv[i + 1] ?? "").trim();
46
+ if (!value) throw new Error("--circle requires a value");
47
+ out.circleMode = value;
48
+ i += 1;
49
+ continue;
50
+ }
51
+ if (arg.startsWith("--circle=")) {
52
+ out.circleMode = arg.slice("--circle=".length).trim();
53
+ continue;
54
+ }
55
+ if (arg === "--workload") {
56
+ const value = String(argv[i + 1] ?? "").trim();
57
+ if (!value) throw new Error("--workload requires a value");
58
+ out.workload = value;
59
+ i += 1;
60
+ continue;
61
+ }
62
+ if (arg.startsWith("--workload=")) {
63
+ out.workload = arg.slice("--workload=".length).trim();
64
+ continue;
65
+ }
66
+ throw new Error(`unknown argument: ${arg}`);
67
+ }
68
+ return out;
69
+ }
70
+
71
+ function readIntEnv(name, fallback) {
72
+ const raw = process.env[name];
73
+ if (raw === undefined || raw === null || String(raw).trim() === "") return fallback;
74
+ const n = Number(raw);
75
+ if (!Number.isSafeInteger(n) || n <= 0) throw new Error(`${name} must be a positive integer`);
76
+ return n;
77
+ }
78
+
79
+ function readBoolEnv(name, fallback) {
80
+ const raw = process.env[name];
81
+ if (raw === undefined || raw === null || String(raw).trim() === "") return fallback;
82
+ const v = String(raw).trim().toLowerCase();
83
+ if (v === "1" || v === "true" || v === "yes") return true;
84
+ if (v === "0" || v === "false" || v === "no") return false;
85
+ throw new Error(`${name} must be a boolean (1/0/true/false)`);
86
+ }
87
+
88
+ function sanitize(text) {
89
+ return String(text ?? "").replaceAll(/[\r\n]+/g, " ").trim();
90
+ }
91
+
92
+ function sanitizeIdSegment(text, { maxLen = 96 } = {}) {
93
+ const raw = String(text ?? "").trim();
94
+ const safe = raw.replaceAll(/[^A-Za-z0-9:_-]/g, "_").slice(0, maxLen);
95
+ return safe || "unknown";
96
+ }
97
+
98
+ function runCommand({ cmd, args, env, timeoutMs = 60_000 }) {
99
+ return new Promise((resolve) => {
100
+ const child = spawn(cmd, args, {
101
+ env: { ...process.env, ...(env ?? {}) },
102
+ stdio: ["ignore", "pipe", "pipe"]
103
+ });
104
+ let stdout = "";
105
+ let stderr = "";
106
+ let settled = false;
107
+ const timer = setTimeout(() => {
108
+ if (settled) return;
109
+ settled = true;
110
+ try {
111
+ child.kill("SIGKILL");
112
+ } catch {
113
+ // ignore
114
+ }
115
+ resolve({
116
+ code: null,
117
+ timeout: true,
118
+ stdout,
119
+ stderr
120
+ });
121
+ }, timeoutMs);
122
+ child.stdout.setEncoding("utf8");
123
+ child.stderr.setEncoding("utf8");
124
+ child.stdout.on("data", (chunk) => {
125
+ stdout += String(chunk);
126
+ });
127
+ child.stderr.on("data", (chunk) => {
128
+ stderr += String(chunk);
129
+ });
130
+ child.once("close", (code) => {
131
+ if (settled) return;
132
+ settled = true;
133
+ clearTimeout(timer);
134
+ resolve({
135
+ code,
136
+ timeout: false,
137
+ stdout,
138
+ stderr
139
+ });
140
+ });
141
+ });
142
+ }
143
+
144
+ function normalizeCircleMode(value) {
145
+ const raw = String(value ?? "").trim().toLowerCase();
146
+ if (!raw || raw === "stub" || raw === "test") return "stub";
147
+ if (raw === "sandbox") return "sandbox";
148
+ if (raw === "production" || raw === "prod") return "production";
149
+ throw new Error("circle mode must be stub|sandbox|production");
150
+ }
151
+
152
+ function normalizeWorkload(value) {
153
+ const raw = String(value ?? "").trim().toLowerCase();
154
+ if (!raw || raw === "exa") return "exa";
155
+ if (raw === "weather") return "weather";
156
+ if (raw === "llm") return "llm";
157
+ throw new Error("workload must be exa|weather|llm");
158
+ }
159
+
160
+ function buildWorkloadUpstreamUrl({ upstreamBaseUrl, workload, toolArgs }) {
161
+ if (workload === "weather") {
162
+ const url = new URL("/weather/current", upstreamBaseUrl);
163
+ url.searchParams.set("city", String(toolArgs?.city ?? "Chicago"));
164
+ url.searchParams.set("unit", String(toolArgs?.unit ?? "f"));
165
+ return url;
166
+ }
167
+ if (workload === "llm") {
168
+ const url = new URL("/llm/completions", upstreamBaseUrl);
169
+ url.searchParams.set("prompt", String(toolArgs?.prompt ?? ""));
170
+ url.searchParams.set("model", String(toolArgs?.model ?? "gpt-4o-mini"));
171
+ url.searchParams.set("maxTokens", String(toolArgs?.maxTokens ?? 128));
172
+ return url;
173
+ }
174
+ const url = new URL("/exa/search", upstreamBaseUrl);
175
+ url.searchParams.set("q", String(toolArgs?.query ?? ""));
176
+ url.searchParams.set("numResults", String(toolArgs?.numResults ?? 3));
177
+ return url;
178
+ }
179
+
180
+ function pickSettldHeaders(headers) {
181
+ const out = {};
182
+ if (!headers || typeof headers.entries !== "function") return out;
183
+ for (const [key, value] of headers.entries()) {
184
+ if (String(key).toLowerCase().startsWith("x-settld-")) out[key] = value;
185
+ }
186
+ return out;
187
+ }
188
+
189
+ function readEnvString(name, fallback = null) {
190
+ const raw = process.env[name];
191
+ if (raw === undefined || raw === null || String(raw).trim() === "") return fallback;
192
+ return String(raw).trim();
193
+ }
194
+
195
+ function assertCircleModeInputs({ mode }) {
196
+ if (mode === "stub") return;
197
+ const required = ["CIRCLE_API_KEY", "CIRCLE_WALLET_ID_SPEND", "CIRCLE_WALLET_ID_ESCROW", "CIRCLE_TOKEN_ID_USDC"];
198
+ const missing = required.filter((name) => !readEnvString(name));
199
+ if (missing.length > 0) {
200
+ throw new Error(`circle mode ${mode} requires env: ${missing.join(", ")}`);
201
+ }
202
+ const hasTemplate = Boolean(readEnvString("CIRCLE_ENTITY_SECRET_CIPHERTEXT_TEMPLATE"));
203
+ const hasStatic = Boolean(readEnvString("CIRCLE_ENTITY_SECRET_CIPHERTEXT")) && readBoolEnv("CIRCLE_ALLOW_STATIC_ENTITY_SECRET", false);
204
+ const hasEntitySecretHex = Boolean(readEnvString("ENTITY_SECRET")) || Boolean(readEnvString("CIRCLE_ENTITY_SECRET_HEX"));
205
+ if (!hasTemplate && !hasStatic && !hasEntitySecretHex) {
206
+ throw new Error(
207
+ `circle mode ${mode} requires ENTITY_SECRET/CIRCLE_ENTITY_SECRET_HEX (preferred), CIRCLE_ENTITY_SECRET_CIPHERTEXT_TEMPLATE, or CIRCLE_ENTITY_SECRET_CIPHERTEXT with CIRCLE_ALLOW_STATIC_ENTITY_SECRET=1`
208
+ );
209
+ }
210
+ }
211
+
212
+ function buildReserveTransitions(gate) {
213
+ const transitions = [];
214
+ const createdAt = typeof gate?.createdAt === "string" && gate.createdAt.trim() !== "" ? gate.createdAt : null;
215
+ const auth = gate?.authorization && typeof gate.authorization === "object" && !Array.isArray(gate.authorization) ? gate.authorization : null;
216
+ const reserve = auth?.reserve && typeof auth.reserve === "object" && !Array.isArray(auth.reserve) ? auth.reserve : null;
217
+
218
+ if (createdAt) {
219
+ transitions.push({
220
+ phase: "gate_created",
221
+ authorizationStatus: "pending",
222
+ reserveStatus: null,
223
+ at: createdAt
224
+ });
225
+ }
226
+
227
+ const reservedAt = typeof reserve?.reservedAt === "string" && reserve.reservedAt.trim() !== "" ? reserve.reservedAt : null;
228
+ if (reserve && reservedAt) {
229
+ transitions.push({
230
+ phase: "authorize_payment",
231
+ authorizationStatus: "reserved",
232
+ reserveStatus: "reserved",
233
+ at: reservedAt
234
+ });
235
+ }
236
+
237
+ const finalAuthStatus = typeof auth?.status === "string" && auth.status.trim() !== "" ? auth.status : null;
238
+ const finalReserveStatus = typeof reserve?.status === "string" && reserve.status.trim() !== "" ? reserve.status : null;
239
+ const finalAt =
240
+ (typeof reserve?.settledAt === "string" && reserve.settledAt.trim() !== "" ? reserve.settledAt : null) ??
241
+ (typeof gate?.resolvedAt === "string" && gate.resolvedAt.trim() !== "" ? gate.resolvedAt : null) ??
242
+ (typeof auth?.updatedAt === "string" && auth.updatedAt.trim() !== "" ? auth.updatedAt : null) ??
243
+ (typeof gate?.updatedAt === "string" && gate.updatedAt.trim() !== "" ? gate.updatedAt : null);
244
+ if (finalAuthStatus || finalReserveStatus || finalAt) {
245
+ const previous = transitions[transitions.length - 1] ?? null;
246
+ const changed =
247
+ !previous ||
248
+ previous.authorizationStatus !== finalAuthStatus ||
249
+ previous.reserveStatus !== finalReserveStatus ||
250
+ previous.at !== finalAt;
251
+ if (changed) {
252
+ transitions.push({
253
+ phase: "gate_verify",
254
+ authorizationStatus: finalAuthStatus,
255
+ reserveStatus: finalReserveStatus,
256
+ at: finalAt
257
+ });
258
+ }
259
+ }
260
+
261
+ return transitions;
262
+ }
263
+
264
+ function log(prefix, msg) {
265
+ process.stderr.write(`[${prefix}] ${msg}\n`);
266
+ }
267
+
268
+ function spawnProc({ name, cmd, args, env }) {
269
+ log(name, `spawn: ${cmd} ${args.join(" ")}`);
270
+ const child = spawn(cmd, args, {
271
+ env: { ...process.env, ...(env ?? {}) },
272
+ stdio: ["ignore", "pipe", "pipe"]
273
+ });
274
+ child.stdout?.on("data", (buf) => {
275
+ const s = sanitize(buf.toString("utf8"));
276
+ if (s) log(name, s);
277
+ });
278
+ child.stderr?.on("data", (buf) => {
279
+ const s = sanitize(buf.toString("utf8"));
280
+ if (s) log(name, s);
281
+ });
282
+ child.on("exit", (code, signal) => {
283
+ log(name, `exit: code=${code} signal=${signal ?? ""}`);
284
+ });
285
+ return child;
286
+ }
287
+
288
+ async function waitForHealth(url, { name, timeoutMs = 30_000, proc = null } = {}) {
289
+ const start = Date.now();
290
+ while (true) {
291
+ if (proc && proc.exitCode !== null) {
292
+ throw new Error(`${name ?? url} exited before becoming ready (exitCode=${proc.exitCode})`);
293
+ }
294
+ try {
295
+ const res = await fetch(url, { method: "GET" });
296
+ if (res.ok) return;
297
+ } catch {
298
+ // retry
299
+ }
300
+ if (Date.now() - start > timeoutMs) {
301
+ throw new Error(`${name ?? url} did not become ready within ${timeoutMs}ms: ${url}`);
302
+ }
303
+ await delay(250);
304
+ }
305
+ }
306
+
307
+ async function mintApiKey({ apiUrl, opsToken, tenantId }) {
308
+ const res = await fetch(new URL("/ops/api-keys", apiUrl), {
309
+ method: "POST",
310
+ headers: {
311
+ "x-proxy-ops-token": opsToken,
312
+ authorization: `Bearer ${opsToken}`,
313
+ "x-proxy-tenant-id": tenantId,
314
+ "content-type": "application/json"
315
+ },
316
+ body: JSON.stringify({
317
+ scopes: ["ops_read", "ops_write", "finance_read", "finance_write", "audit_read"],
318
+ description: "mcp paid tool demo"
319
+ })
320
+ });
321
+ const text = await res.text();
322
+ let json = null;
323
+ try {
324
+ json = text ? JSON.parse(text) : null;
325
+ } catch {
326
+ json = null;
327
+ }
328
+ if (!res.ok) throw new Error(`mint api key failed: HTTP ${res.status} ${text}`);
329
+ const keyId = json?.keyId;
330
+ const secret = json?.secret;
331
+ if (typeof keyId !== "string" || typeof secret !== "string" || !keyId || !secret) {
332
+ throw new Error(`mint api key returned unexpected body: ${text}`);
333
+ }
334
+ return `${keyId}.${secret}`;
335
+ }
336
+
337
+ async function runMcpToolCall({
338
+ baseUrl,
339
+ tenantId,
340
+ apiKey,
341
+ paidToolsBaseUrl,
342
+ toolName,
343
+ toolArgs,
344
+ timeoutMs = 20_000
345
+ }) {
346
+ const child = spawn(process.execPath, ["scripts/mcp/settld-mcp-server.mjs"], {
347
+ env: {
348
+ ...process.env,
349
+ SETTLD_BASE_URL: baseUrl,
350
+ SETTLD_TENANT_ID: tenantId,
351
+ SETTLD_API_KEY: apiKey,
352
+ SETTLD_PROTOCOL: "1.0",
353
+ SETTLD_PAID_TOOLS_BASE_URL: paidToolsBaseUrl
354
+ },
355
+ stdio: ["pipe", "pipe", "pipe"]
356
+ });
357
+
358
+ child.stdout.setEncoding("utf8");
359
+ child.stderr.setEncoding("utf8");
360
+
361
+ let stderrBuf = "";
362
+ child.stderr.on("data", (chunk) => {
363
+ stderrBuf += String(chunk);
364
+ });
365
+
366
+ const pending = new Map();
367
+ let buf = "";
368
+ child.stdout.on("data", (chunk) => {
369
+ buf += chunk;
370
+ for (;;) {
371
+ const idx = buf.indexOf("\n");
372
+ if (idx === -1) break;
373
+ const line = buf.slice(0, idx).trim();
374
+ buf = buf.slice(idx + 1);
375
+ if (!line) continue;
376
+ let msg = null;
377
+ try {
378
+ msg = JSON.parse(line);
379
+ } catch {
380
+ continue;
381
+ }
382
+ const id = msg?.id;
383
+ if (id !== undefined && id !== null && pending.has(String(id))) {
384
+ const item = pending.get(String(id));
385
+ pending.delete(String(id));
386
+ item.resolve(msg);
387
+ }
388
+ }
389
+ });
390
+
391
+ function rpc(method, params = {}) {
392
+ const id = String(Math.random()).slice(2);
393
+ const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params });
394
+ child.stdin.write(payload + "\n");
395
+ return new Promise((resolve, reject) => {
396
+ pending.set(id, { resolve, reject });
397
+ setTimeout(() => {
398
+ if (!pending.has(id)) return;
399
+ pending.delete(id);
400
+ reject(new Error(`timeout waiting for ${method}`));
401
+ }, timeoutMs).unref?.();
402
+ });
403
+ }
404
+
405
+ try {
406
+ const initialize = await rpc("initialize", {
407
+ protocolVersion: "2024-11-05",
408
+ clientInfo: { name: "settld-demo-mcp-paid-exa", version: "s1" },
409
+ capabilities: {}
410
+ });
411
+ const called = await rpc("tools/call", {
412
+ name: toolName,
413
+ arguments: toolArgs
414
+ });
415
+
416
+ const text = called?.result?.content?.[0]?.text ?? "";
417
+ let parsed = null;
418
+ try {
419
+ parsed = text ? JSON.parse(text) : null;
420
+ } catch {
421
+ parsed = null;
422
+ }
423
+
424
+ return {
425
+ initialize,
426
+ called,
427
+ parsed,
428
+ stderr: stderrBuf
429
+ };
430
+ } finally {
431
+ try {
432
+ child.kill("SIGTERM");
433
+ } catch {
434
+ // ignore
435
+ }
436
+ await Promise.race([delay(100), new Promise((resolve) => child.once("exit", resolve))]);
437
+ }
438
+ }
439
+
440
+ async function writeArtifactJson(dir, filename, value) {
441
+ await writeFile(path.join(dir, filename), JSON.stringify(value, null, 2) + "\n", "utf8");
442
+ }
443
+
444
+ function parseJsonLines(text) {
445
+ return String(text ?? "")
446
+ .split(/\r?\n/)
447
+ .map((line) => line.trim())
448
+ .filter(Boolean)
449
+ .map((line) => JSON.parse(line));
450
+ }
451
+
452
+ function reportCheckOk(report, checkId) {
453
+ const checks = Array.isArray(report?.checks) ? report.checks : [];
454
+ const row = checks.find((item) => item && item.id === checkId);
455
+ return row?.ok === true;
456
+ }
457
+
458
+ function receiptVerificationCoreOk(report) {
459
+ return (
460
+ reportCheckOk(report, "settlement_kernel_artifacts") &&
461
+ reportCheckOk(report, "request_hash_binding") &&
462
+ reportCheckOk(report, "response_hash_binding") &&
463
+ reportCheckOk(report, "provider_output_signature_crypto")
464
+ );
465
+ }
466
+
467
+ async function exportAndVerifyReceiptSample({
468
+ artifactDir,
469
+ apiUrl,
470
+ apiKey,
471
+ tenantId
472
+ }) {
473
+ const exportRes = await fetch(new URL("/x402/receipts/export?limit=25", apiUrl), {
474
+ method: "GET",
475
+ headers: {
476
+ authorization: `Bearer ${apiKey}`,
477
+ "x-proxy-tenant-id": tenantId,
478
+ "x-settld-protocol": "1.0",
479
+ accept: "application/json"
480
+ }
481
+ });
482
+ const exportBody = await exportRes.text();
483
+ if (!exportRes.ok) {
484
+ throw new Error(`x402 receipt export failed: HTTP ${exportRes.status} ${sanitize(exportBody).slice(0, 300)}`);
485
+ }
486
+
487
+ const exportPath = path.join(artifactDir, "x402-receipts.export.jsonl");
488
+ const normalizedExportBody = exportBody.endsWith("\n") ? exportBody : `${exportBody}\n`;
489
+ await writeFile(exportPath, normalizedExportBody, "utf8");
490
+
491
+ const receipts = parseJsonLines(exportBody);
492
+ const sampleReceipt = receipts[0] ?? null;
493
+ const sampleDecisionId =
494
+ typeof sampleReceipt?.decisionRecord?.decisionId === "string" && sampleReceipt.decisionRecord.decisionId.trim() !== ""
495
+ ? sampleReceipt.decisionRecord.decisionId.trim()
496
+ : typeof sampleReceipt?.settlementReceipt?.decisionRef?.decisionId === "string" &&
497
+ sampleReceipt.settlementReceipt.decisionRef.decisionId.trim() !== ""
498
+ ? sampleReceipt.settlementReceipt.decisionRef.decisionId.trim()
499
+ : null;
500
+ const sampleSettlementReceiptId =
501
+ typeof sampleReceipt?.settlementReceipt?.receiptId === "string" && sampleReceipt.settlementReceipt.receiptId.trim() !== ""
502
+ ? sampleReceipt.settlementReceipt.receiptId.trim()
503
+ : typeof sampleReceipt?.receiptId === "string" && sampleReceipt.receiptId.trim() !== ""
504
+ ? sampleReceipt.receiptId.trim()
505
+ : null;
506
+ const sampleVerification =
507
+ sampleReceipt && typeof sampleReceipt === "object" && !Array.isArray(sampleReceipt)
508
+ ? {
509
+ nonStrict: verifyX402ReceiptRecord({ receipt: sampleReceipt, strict: false }),
510
+ strict: verifyX402ReceiptRecord({ receipt: sampleReceipt, strict: true })
511
+ }
512
+ : null;
513
+
514
+ const verificationArtifact = {
515
+ schemaVersion: "X402ReceiptSampleVerification.v1",
516
+ generatedAt: new Date().toISOString(),
517
+ exportPath: "x402-receipts.export.jsonl",
518
+ exportedReceiptCount: receipts.length,
519
+ sampleReceiptId: typeof sampleReceipt?.receiptId === "string" ? sampleReceipt.receiptId : null,
520
+ sampleDecisionId,
521
+ sampleSettlementReceiptId,
522
+ sampleVerification,
523
+ sampleVerificationCoreOk: receiptVerificationCoreOk(sampleVerification?.nonStrict)
524
+ };
525
+ await writeArtifactJson(artifactDir, "x402-receipts.sample-verification.json", verificationArtifact);
526
+
527
+ return {
528
+ ok:
529
+ receipts.length > 0 &&
530
+ receiptVerificationCoreOk(sampleVerification?.nonStrict),
531
+ nonStrictOk: Boolean(sampleVerification?.nonStrict?.ok),
532
+ strictOk: Boolean(sampleVerification?.strict?.ok),
533
+ exportedReceiptCount: receipts.length,
534
+ sampleReceiptId: verificationArtifact.sampleReceiptId,
535
+ sampleDecisionId,
536
+ sampleSettlementReceiptId
537
+ };
538
+ }
539
+
540
+ async function runBatchSettlementDemo({
541
+ artifactDir,
542
+ providerId,
543
+ circleMode,
544
+ enabled
545
+ }) {
546
+ if (!enabled) {
547
+ return {
548
+ enabled: false,
549
+ ok: true,
550
+ skipped: true,
551
+ reason: "disabled"
552
+ };
553
+ }
554
+
555
+ const providerWalletId = readEnvString("SETTLD_DEMO_BATCH_PROVIDER_WALLET_ID", readEnvString("CIRCLE_WALLET_ID_ESCROW", null));
556
+ if (circleMode !== "stub" && !providerWalletId) {
557
+ throw new Error("batch settlement demo requires SETTLD_DEMO_BATCH_PROVIDER_WALLET_ID or CIRCLE_WALLET_ID_ESCROW");
558
+ }
559
+
560
+ const registryPath = path.join(artifactDir, "batch-payout-registry.json");
561
+ const statePath = path.join(artifactDir, "batch-worker-state.json");
562
+ const outDir = path.join(artifactDir, "batch-settlement-run");
563
+ const artifactRoot = path.dirname(artifactDir);
564
+ const registry = {
565
+ schemaVersion: "X402ProviderPayoutRegistry.v1",
566
+ providers: [
567
+ {
568
+ providerId,
569
+ destination: {
570
+ type: "circle_wallet",
571
+ walletId: providerWalletId ?? "wallet_demo_stub",
572
+ blockchain: readEnvString("CIRCLE_BLOCKCHAIN", circleMode === "production" ? "BASE" : "BASE-SEPOLIA"),
573
+ token: "USDC"
574
+ }
575
+ }
576
+ ]
577
+ };
578
+ await writeArtifactJson(artifactDir, "batch-payout-registry.json", registry);
579
+
580
+ const args = [
581
+ "scripts/settlement/x402-batch-worker.mjs",
582
+ "--artifact-root",
583
+ artifactRoot,
584
+ "--registry",
585
+ registryPath,
586
+ "--state",
587
+ statePath,
588
+ "--out-dir",
589
+ outDir,
590
+ "--execute-circle",
591
+ "--circle-mode",
592
+ circleMode
593
+ ];
594
+ const run = await runCommand({
595
+ cmd: "node",
596
+ args,
597
+ timeoutMs: readIntEnv("SETTLD_DEMO_BATCH_SETTLEMENT_TIMEOUT_MS", 90_000)
598
+ });
599
+ if (run.timeout) throw new Error("batch settlement command timed out");
600
+ if (run.code !== 0) {
601
+ throw new Error(`batch settlement failed (exit=${run.code}): ${sanitize(run.stderr) || sanitize(run.stdout)}`);
602
+ }
603
+ const stdoutLines = String(run.stdout ?? "")
604
+ .split(/\r?\n/)
605
+ .map((line) => line.trim())
606
+ .filter(Boolean);
607
+ const lastLine = stdoutLines[stdoutLines.length - 1] ?? "";
608
+ let parsed = null;
609
+ try {
610
+ parsed = lastLine ? JSON.parse(lastLine) : null;
611
+ } catch {
612
+ parsed = null;
613
+ }
614
+ if (!parsed || parsed.ok !== true) {
615
+ throw new Error(`batch settlement returned unexpected output: ${sanitize(run.stdout).slice(0, 300)}`);
616
+ }
617
+
618
+ const artifact = {
619
+ enabled: true,
620
+ ok: true,
621
+ circleMode,
622
+ command: ["node", ...args],
623
+ artifactRoot,
624
+ registryPath,
625
+ statePath,
626
+ outDir,
627
+ result: parsed
628
+ };
629
+ await writeArtifactJson(artifactDir, "batch-settlement.json", artifact);
630
+ return artifact;
631
+ }
632
+
633
+ async function main() {
634
+ const cli = parseCliArgs(process.argv.slice(2));
635
+ if (cli.help) {
636
+ process.stdout.write(`${usage()}\n`);
637
+ return;
638
+ }
639
+
640
+ const apiPort = readIntEnv("SETTLD_DEMO_API_PORT", 3000);
641
+ const upstreamPort = readIntEnv("SETTLD_DEMO_UPSTREAM_PORT", 9402);
642
+ const gatewayPort = readIntEnv("SETTLD_DEMO_GATEWAY_PORT", 8402);
643
+ const keepAlive = readBoolEnv("SETTLD_DEMO_KEEP_ALIVE", false);
644
+ const runBatchSettlement = readBoolEnv("SETTLD_DEMO_RUN_BATCH_SETTLEMENT", false);
645
+ const circleMode = normalizeCircleMode(cli.circleMode ?? readEnvString("SETTLD_DEMO_CIRCLE_MODE", "stub"));
646
+ const workload = normalizeWorkload(cli.workload ?? readEnvString("SETTLD_DEMO_WORKLOAD", "exa"));
647
+ const externalReserveRequired = circleMode !== "stub";
648
+ assertCircleModeInputs({ mode: circleMode });
649
+ const opsToken = String(process.env.SETTLD_DEMO_OPS_TOKEN ?? "tok_ops").trim() || "tok_ops";
650
+ const tenantId = String(process.env.SETTLD_TENANT_ID ?? "tenant_default").trim() || "tenant_default";
651
+
652
+ const workloadConfig = (() => {
653
+ if (workload === "weather") {
654
+ const city = String(process.env.SETTLD_DEMO_CITY ?? "Chicago").trim() || "Chicago";
655
+ const unitRaw = String(process.env.SETTLD_DEMO_UNIT ?? "f").trim().toLowerCase();
656
+ const unit = unitRaw === "c" ? "c" : "f";
657
+ return {
658
+ toolName: "settld.weather_current_paid",
659
+ toolArgs: { city, unit },
660
+ description: `weather city=${city} unit=${unit}`
661
+ };
662
+ }
663
+ if (workload === "llm") {
664
+ const prompt = String(process.env.SETTLD_DEMO_PROMPT ?? "Summarize why deferred settlement matters for paid API calls.").trim();
665
+ const model = String(process.env.SETTLD_DEMO_MODEL ?? "gpt-4o-mini").trim() || "gpt-4o-mini";
666
+ const maxTokens = readIntEnv("SETTLD_DEMO_MAX_TOKENS", 128);
667
+ return {
668
+ toolName: "settld.llm_completion_paid",
669
+ toolArgs: { prompt, model, maxTokens },
670
+ description: `llm model=${model} maxTokens=${maxTokens}`
671
+ };
672
+ }
673
+ const query = String(process.env.SETTLD_DEMO_QUERY ?? "dentist near me chicago").trim() || "dentist near me chicago";
674
+ const numResults = readIntEnv("SETTLD_DEMO_NUM_RESULTS", 3);
675
+ return {
676
+ toolName: "settld.exa_search_paid",
677
+ toolArgs: { query, numResults },
678
+ description: `exa query=${query} numResults=${numResults}`
679
+ };
680
+ })();
681
+
682
+ const now = new Date();
683
+ const runId = now.toISOString().replaceAll(":", "").replaceAll(".", "");
684
+ const artifactDir =
685
+ process.env.SETTLD_DEMO_ARTIFACT_DIR && String(process.env.SETTLD_DEMO_ARTIFACT_DIR).trim() !== ""
686
+ ? String(process.env.SETTLD_DEMO_ARTIFACT_DIR).trim()
687
+ : path.join("artifacts", `mcp-paid-${workload}`, runId);
688
+
689
+ const apiUrl = new URL(`http://127.0.0.1:${apiPort}`);
690
+ const upstreamUrl = new URL(`http://127.0.0.1:${upstreamPort}`);
691
+ const gatewayUrl = new URL(`http://127.0.0.1:${gatewayPort}`);
692
+ const providerId = `agt_x402_payee_${sanitizeIdSegment(upstreamUrl.host)}`;
693
+
694
+ await mkdir(artifactDir, { recursive: true });
695
+
696
+ const procs = [];
697
+ const stopAll = () => {
698
+ for (const p of procs) {
699
+ try {
700
+ p.kill("SIGTERM");
701
+ } catch {
702
+ // ignore
703
+ }
704
+ }
705
+ };
706
+
707
+ let summary = {
708
+ ok: false,
709
+ runId,
710
+ artifactDir,
711
+ workload,
712
+ toolName: workloadConfig.toolName,
713
+ toolArgs: workloadConfig.toolArgs,
714
+ providerId,
715
+ circleMode,
716
+ timestamps: { startedAt: now.toISOString(), completedAt: null }
717
+ };
718
+
719
+ process.on("SIGINT", () => {
720
+ log("demo", "SIGINT: shutting down...");
721
+ stopAll();
722
+ process.exit(130);
723
+ });
724
+ process.on("SIGTERM", () => {
725
+ log("demo", "SIGTERM: shutting down...");
726
+ stopAll();
727
+ process.exit(143);
728
+ });
729
+
730
+ try {
731
+ const api = spawnProc({
732
+ name: "api",
733
+ cmd: "node",
734
+ args: ["src/api/server.js"],
735
+ env: {
736
+ PROXY_OPS_TOKEN: opsToken,
737
+ BIND_HOST: "127.0.0.1",
738
+ PORT: String(apiPort),
739
+ X402_CIRCLE_RESERVE_MODE: circleMode,
740
+ X402_REQUIRE_EXTERNAL_RESERVE: externalReserveRequired ? "1" : "0"
741
+ }
742
+ });
743
+ procs.push(api);
744
+ await waitForHealth(new URL("/healthz", apiUrl).toString(), { name: "api /healthz", proc: api });
745
+
746
+ const apiKey = await mintApiKey({ apiUrl, opsToken, tenantId });
747
+ log("demo", "SETTLD_API_KEY minted");
748
+
749
+ const upstream = spawnProc({
750
+ name: "upstream",
751
+ cmd: "node",
752
+ args: ["services/x402-gateway/examples/upstream-mock.js"],
753
+ env: {
754
+ BIND_HOST: "127.0.0.1",
755
+ PORT: String(upstreamPort),
756
+ SETTLD_PROVIDER_ID: providerId,
757
+ SETTLD_PAY_KEYSET_URL: new URL("/.well-known/settld-keys.json", apiUrl).toString()
758
+ }
759
+ });
760
+ procs.push(upstream);
761
+ await waitForHealth(new URL("/healthz", upstreamUrl).toString(), { name: "upstream /healthz", proc: upstream });
762
+
763
+ const providerKeyRes = await fetch(new URL("/settld/provider-key", upstreamUrl));
764
+ if (!providerKeyRes.ok) throw new Error(`provider key fetch failed: HTTP ${providerKeyRes.status}`);
765
+ const providerKey = await providerKeyRes.json();
766
+ const providerPublicKeyPem = typeof providerKey?.publicKeyPem === "string" ? providerKey.publicKeyPem : null;
767
+ if (!providerPublicKeyPem) throw new Error("provider did not return publicKeyPem");
768
+
769
+ const gateway = spawnProc({
770
+ name: "gateway",
771
+ cmd: "node",
772
+ args: ["services/x402-gateway/src/server.js"],
773
+ env: {
774
+ BIND_HOST: "127.0.0.1",
775
+ SETTLD_API_URL: apiUrl.toString(),
776
+ SETTLD_API_KEY: apiKey,
777
+ UPSTREAM_URL: upstreamUrl.toString(),
778
+ X402_AUTOFUND: "1",
779
+ X402_PROVIDER_PUBLIC_KEY_PEM: providerPublicKeyPem,
780
+ PORT: String(gatewayPort)
781
+ }
782
+ });
783
+ procs.push(gateway);
784
+ await waitForHealth(new URL("/healthz", gatewayUrl).toString(), { name: "gateway /healthz", proc: gateway });
785
+
786
+ const mcp = await runMcpToolCall({
787
+ baseUrl: apiUrl.toString(),
788
+ tenantId,
789
+ apiKey,
790
+ paidToolsBaseUrl: gatewayUrl.toString(),
791
+ toolName: workloadConfig.toolName,
792
+ toolArgs: workloadConfig.toolArgs
793
+ });
794
+ await writeArtifactJson(artifactDir, "mcp-call.raw.json", mcp.called);
795
+ await writeArtifactJson(artifactDir, "mcp-call.parsed.json", mcp.parsed ?? {});
796
+
797
+ if (mcp.called?.result?.isError) {
798
+ throw new Error(`mcp tool call returned error: ${mcp.called?.result?.content?.[0]?.text ?? "unknown"}`);
799
+ }
800
+ if (!mcp.parsed?.result || typeof mcp.parsed.result !== "object") {
801
+ throw new Error("mcp parsed result missing");
802
+ }
803
+
804
+ const result = mcp.parsed.result;
805
+ const responseBody = result.response ?? null;
806
+ const headers = result.headers ?? {};
807
+ const gateId = typeof headers["x-settld-gate-id"] === "string" ? headers["x-settld-gate-id"] : "";
808
+ if (!gateId) throw new Error("missing x-settld-gate-id from paid response headers");
809
+ await writeArtifactJson(artifactDir, "response-body.json", responseBody ?? {});
810
+
811
+ const gateStateRes = await fetch(new URL(`/x402/gate/${encodeURIComponent(gateId)}`, apiUrl), {
812
+ method: "GET",
813
+ headers: {
814
+ authorization: `Bearer ${apiKey}`,
815
+ "x-proxy-tenant-id": tenantId,
816
+ "x-settld-protocol": "1.0"
817
+ }
818
+ });
819
+ const gateStateText = await gateStateRes.text();
820
+ let gateState = null;
821
+ try {
822
+ gateState = gateStateText ? JSON.parse(gateStateText) : null;
823
+ } catch {
824
+ gateState = null;
825
+ }
826
+ if (!gateStateRes.ok) throw new Error(`gate state fetch failed: HTTP ${gateStateRes.status} ${gateStateText}`);
827
+ await writeArtifactJson(artifactDir, "gate-state.json", gateState ?? {});
828
+
829
+ const reserveState = (() => {
830
+ const gate = gateState?.gate ?? null;
831
+ const auth = gate?.authorization && typeof gate.authorization === "object" && !Array.isArray(gate.authorization) ? gate.authorization : null;
832
+ const reserve = auth?.reserve && typeof auth.reserve === "object" && !Array.isArray(auth.reserve) ? auth.reserve : null;
833
+ const circleReserveId =
834
+ typeof reserve?.reserveId === "string" && reserve.reserveId.trim() !== ""
835
+ ? reserve.reserveId.trim()
836
+ : typeof reserve?.circleTransferId === "string" && reserve.circleTransferId.trim() !== ""
837
+ ? reserve.circleTransferId.trim()
838
+ : null;
839
+ return {
840
+ mode: circleMode,
841
+ externalReserveRequired,
842
+ authorizationRef: typeof auth?.authorizationRef === "string" ? auth.authorizationRef : null,
843
+ circleReserveId,
844
+ reserve,
845
+ transitions: buildReserveTransitions(gate),
846
+ rail: {
847
+ provider: "circle",
848
+ mode: circleMode,
849
+ blockchain: readEnvString("CIRCLE_BLOCKCHAIN", circleMode === "production" ? "BASE" : "BASE-SEPOLIA"),
850
+ tokenId: readEnvString("CIRCLE_TOKEN_ID_USDC", null),
851
+ spendWalletId: readEnvString("CIRCLE_WALLET_ID_SPEND", null),
852
+ escrowWalletId: readEnvString("CIRCLE_WALLET_ID_ESCROW", null)
853
+ },
854
+ payoutDestination: {
855
+ type: "agent_wallet",
856
+ payeeAgentId: providerId,
857
+ note: "Batch payout worker is not enabled in this demo run."
858
+ }
859
+ };
860
+ })();
861
+ await writeArtifactJson(artifactDir, "reserve-state.json", reserveState);
862
+
863
+ // Write a provisional summary so artifact-driven settlement workers can
864
+ // discover this run before final summary enrichment.
865
+ const preBatchSummary = {
866
+ ...summary,
867
+ ok: true,
868
+ gateId,
869
+ workload,
870
+ toolName: workloadConfig.toolName,
871
+ toolArgs: workloadConfig.toolArgs,
872
+ circleReserveId: reserveState.circleReserveId,
873
+ reserveTransitions: reserveState.transitions,
874
+ payoutDestination: reserveState.payoutDestination,
875
+ artifactFiles: [
876
+ "mcp-call.raw.json",
877
+ "mcp-call.parsed.json",
878
+ "response-body.json",
879
+ "gate-state.json",
880
+ "reserve-state.json"
881
+ ]
882
+ };
883
+ await writeArtifactJson(artifactDir, "summary.json", preBatchSummary);
884
+
885
+ const providerSignatureVerification = (() => {
886
+ const responseHash = sha256Hex(canonicalJsonStringify(responseBody ?? {}));
887
+ const signature = {
888
+ schemaVersion: "ToolProviderSignature.v1",
889
+ algorithm: "ed25519",
890
+ keyId: String(headers["x-settld-provider-key-id"] ?? ""),
891
+ signedAt: String(headers["x-settld-provider-signed-at"] ?? ""),
892
+ nonce: String(headers["x-settld-provider-nonce"] ?? ""),
893
+ responseHash,
894
+ payloadHash: computeToolProviderSignaturePayloadHashV1({
895
+ responseHash,
896
+ nonce: String(headers["x-settld-provider-nonce"] ?? ""),
897
+ signedAt: String(headers["x-settld-provider-signed-at"] ?? "")
898
+ }),
899
+ signatureBase64: String(headers["x-settld-provider-signature"] ?? "")
900
+ };
901
+ let ok = false;
902
+ let error = null;
903
+ try {
904
+ ok = verifyToolProviderSignatureV1({ signature, publicKeyPem: providerPublicKeyPem });
905
+ } catch (err) {
906
+ ok = false;
907
+ error = err?.message ?? String(err ?? "");
908
+ }
909
+ return {
910
+ ok,
911
+ error,
912
+ responseHashExpected: responseHash,
913
+ responseHashHeader: String(headers["x-settld-provider-response-sha256"] ?? ""),
914
+ signature
915
+ };
916
+ })();
917
+ await writeArtifactJson(artifactDir, "provider-signature-verification.json", providerSignatureVerification);
918
+
919
+ const tokenVerification = await (async () => {
920
+ const token = gateState?.gate?.authorization?.token?.value;
921
+ if (typeof token !== "string" || token.trim() === "") return { ok: false, skipped: true, reason: "token_missing" };
922
+ const keysetRes = await fetch(new URL("/.well-known/settld-keys.json", apiUrl));
923
+ const keysetText = await keysetRes.text();
924
+ let keyset = null;
925
+ try {
926
+ keyset = keysetText ? JSON.parse(keysetText) : null;
927
+ } catch {
928
+ keyset = null;
929
+ }
930
+ if (!keysetRes.ok || !keyset) return { ok: false, skipped: true, reason: "keyset_unavailable", status: keysetRes.status };
931
+ let verified = null;
932
+ try {
933
+ verified = verifySettldPayTokenV1({
934
+ token,
935
+ keyset,
936
+ expectedAudience: String(gateState?.gate?.payeeAgentId ?? ""),
937
+ expectedPayeeProviderId: String(gateState?.gate?.payeeAgentId ?? "")
938
+ });
939
+ } catch (err) {
940
+ return { ok: false, skipped: false, code: "VERIFY_THROW", message: err?.message ?? String(err ?? "") };
941
+ }
942
+ return { ok: Boolean(verified?.ok), verification: verified };
943
+ })();
944
+ await writeArtifactJson(artifactDir, "settld-pay-token-verification.json", tokenVerification);
945
+
946
+ const replayProbe = await (async () => {
947
+ const token = gateState?.gate?.authorization?.token?.value;
948
+ if (typeof token !== "string" || token.trim() === "") {
949
+ return {
950
+ attempted: false,
951
+ skipped: true,
952
+ reason: "token_missing"
953
+ };
954
+ }
955
+ const probeUrl = buildWorkloadUpstreamUrl({
956
+ upstreamBaseUrl: upstreamUrl,
957
+ workload,
958
+ toolArgs: workloadConfig.toolArgs
959
+ });
960
+ const res = await fetch(probeUrl, {
961
+ method: "GET",
962
+ headers: {
963
+ authorization: `SettldPay ${token}`,
964
+ "x-proxy-tenant-id": tenantId
965
+ }
966
+ });
967
+ const text = await res.text();
968
+ let json = null;
969
+ try {
970
+ json = text ? JSON.parse(text) : null;
971
+ } catch {
972
+ json = null;
973
+ }
974
+ const headers = pickSettldHeaders(res.headers);
975
+ const duplicate = String(headers["x-settld-provider-replay"] ?? "").toLowerCase() === "duplicate";
976
+ return {
977
+ attempted: true,
978
+ skipped: false,
979
+ url: probeUrl.toString(),
980
+ statusCode: res.status,
981
+ duplicate,
982
+ headers,
983
+ body: json ?? text
984
+ };
985
+ })();
986
+ const replayCounters = {
987
+ schemaVersion: "X402ProviderReplayCounters.v1",
988
+ totalRequests: replayProbe.attempted ? 1 : 0,
989
+ duplicateResponses: replayProbe.attempted && replayProbe.duplicate ? 1 : 0,
990
+ duplicateRate: replayProbe.attempted ? (replayProbe.duplicate ? 1 : 0) : null
991
+ };
992
+ await writeArtifactJson(artifactDir, "provider-replay-probe.json", {
993
+ replayProbe,
994
+ replayCounters
995
+ });
996
+
997
+ const receiptExport = await exportAndVerifyReceiptSample({
998
+ artifactDir,
999
+ apiUrl,
1000
+ apiKey,
1001
+ tenantId
1002
+ });
1003
+
1004
+ const batchSettlement = await runBatchSettlementDemo({
1005
+ artifactDir,
1006
+ providerId,
1007
+ circleMode,
1008
+ enabled: runBatchSettlement
1009
+ });
1010
+
1011
+ const passChecks = {
1012
+ settlementStatus: String(headers["x-settld-settlement-status"] ?? "") === "released",
1013
+ verificationStatus: String(headers["x-settld-verification-status"] ?? "") === "green",
1014
+ providerSignature: providerSignatureVerification.ok === true,
1015
+ tokenVerified: tokenVerification.ok === true,
1016
+ batchSettlement: batchSettlement.ok === true,
1017
+ receiptExport: receiptExport.ok === true,
1018
+ receiptVerifier: receiptExport.ok === true,
1019
+ reserveTracked:
1020
+ typeof reserveState?.circleReserveId === "string" &&
1021
+ reserveState.circleReserveId.trim() !== "" &&
1022
+ Array.isArray(reserveState?.transitions) &&
1023
+ reserveState.transitions.length >= 2
1024
+ };
1025
+
1026
+ summary = {
1027
+ ...summary,
1028
+ ok: Object.values(passChecks).every(Boolean),
1029
+ passChecks,
1030
+ gateId,
1031
+ workload,
1032
+ toolName: workloadConfig.toolName,
1033
+ toolArgs: workloadConfig.toolArgs,
1034
+ workloadDescription: workloadConfig.description,
1035
+ circleReserveId: reserveState.circleReserveId,
1036
+ reserveTransitions: reserveState.transitions,
1037
+ payoutDestination: reserveState.payoutDestination,
1038
+ replayCounters,
1039
+ replayProbe,
1040
+ receiptExport,
1041
+ batchSettlement,
1042
+ artifactFiles: [
1043
+ "mcp-call.raw.json",
1044
+ "mcp-call.parsed.json",
1045
+ "response-body.json",
1046
+ "gate-state.json",
1047
+ "reserve-state.json",
1048
+ "provider-signature-verification.json",
1049
+ "settld-pay-token-verification.json",
1050
+ "provider-replay-probe.json",
1051
+ "x402-receipts.export.jsonl",
1052
+ "x402-receipts.sample-verification.json",
1053
+ ...(runBatchSettlement ? ["batch-payout-registry.json", "batch-worker-state.json", "batch-settlement.json"] : [])
1054
+ ],
1055
+ timestamps: {
1056
+ ...summary.timestamps,
1057
+ completedAt: new Date().toISOString()
1058
+ }
1059
+ };
1060
+
1061
+ await writeArtifactJson(artifactDir, "summary.json", summary);
1062
+
1063
+ if (!summary.ok) {
1064
+ process.stdout.write(`FAIL artifactDir=${artifactDir}\n`);
1065
+ process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
1066
+ process.exitCode = 1;
1067
+ return;
1068
+ }
1069
+
1070
+ process.stdout.write(`PASS artifactDir=${artifactDir}\n`);
1071
+ process.stdout.write(`gateId=${gateId}\n`);
1072
+ if (typeof receiptExport.sampleDecisionId === "string" && receiptExport.sampleDecisionId.trim() !== "") {
1073
+ process.stdout.write(`decisionId=${receiptExport.sampleDecisionId.trim()}\n`);
1074
+ }
1075
+ if (typeof receiptExport.sampleSettlementReceiptId === "string" && receiptExport.sampleSettlementReceiptId.trim() !== "") {
1076
+ process.stdout.write(`settlementReceiptId=${receiptExport.sampleSettlementReceiptId.trim()}\n`);
1077
+ }
1078
+
1079
+ if (!keepAlive) {
1080
+ stopAll();
1081
+ return;
1082
+ }
1083
+
1084
+ log("demo", "Services are running. Press Ctrl+C to stop.");
1085
+ // eslint-disable-next-line no-constant-condition
1086
+ while (true) await delay(1000);
1087
+ } catch (err) {
1088
+ summary = {
1089
+ ...summary,
1090
+ ok: false,
1091
+ error: err?.message ?? String(err ?? ""),
1092
+ timestamps: {
1093
+ ...summary.timestamps,
1094
+ completedAt: new Date().toISOString()
1095
+ }
1096
+ };
1097
+ try {
1098
+ await writeArtifactJson(artifactDir, "summary.json", summary);
1099
+ } catch {
1100
+ // ignore
1101
+ }
1102
+ process.stdout.write(`FAIL artifactDir=${artifactDir}\n`);
1103
+ process.stderr.write(`${err?.stack ?? err?.message ?? String(err ?? "")}\n`);
1104
+ process.exitCode = 1;
1105
+ } finally {
1106
+ if (!keepAlive) stopAll();
1107
+ }
1108
+ }
1109
+
1110
+ main();