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,235 @@
1
+ /**
2
+ * Seeds a deterministic workload into Postgres using the in-process API.
3
+ *
4
+ * Outputs JSON with:
5
+ * - tenantId
6
+ * - jobIds
7
+ * - month
8
+ */
9
+ import { createBackupRestoreApiClient } from "./client.mjs";
10
+ import { makeBookedPayload } from "../../test/api-test-harness.js";
11
+
12
+ const DATABASE_URL = process.env.DATABASE_URL;
13
+ if (!DATABASE_URL) throw new Error("DATABASE_URL is required");
14
+
15
+ const TENANT_ID = process.env.TENANT_ID ?? "tenant_default";
16
+ const MONTH = process.env.MONTH ?? "2026-01";
17
+ const JOBS = Number(process.env.JOBS ?? "10");
18
+ const SCHEMA = process.env.PROXY_PG_SCHEMA ?? "public";
19
+ const requireMonthCloseRaw = String(process.env.BACKUP_RESTORE_REQUIRE_MONTH_CLOSE ?? "").trim().toLowerCase();
20
+ const REQUIRE_MONTH_CLOSE = requireMonthCloseRaw === "" ? true : requireMonthCloseRaw === "1" || requireMonthCloseRaw === "true";
21
+
22
+ function log(message, details = null) {
23
+ const at = new Date().toISOString();
24
+ if (details && typeof details === "object") {
25
+ process.stderr.write(`[seed-workload ${at}] ${message} ${JSON.stringify(details)}\n`);
26
+ return;
27
+ }
28
+ process.stderr.write(`[seed-workload ${at}] ${message}\n`);
29
+ }
30
+
31
+ if (!Number.isSafeInteger(JOBS) || JOBS <= 0) throw new Error("JOBS must be a positive integer");
32
+ if (!/^\d{4}-\d{2}$/.test(MONTH)) throw new Error("MONTH must match YYYY-MM");
33
+
34
+ let nowMs = Date.parse(`${MONTH}-15T10:00:00.000Z`);
35
+ if (!Number.isFinite(nowMs)) throw new Error("invalid MONTH timestamp anchor");
36
+ const nowIso = () => new Date(nowMs).toISOString();
37
+
38
+ const { api, request, close, tenantId } = await createBackupRestoreApiClient({
39
+ databaseUrl: DATABASE_URL,
40
+ schema: SCHEMA,
41
+ tenantId: TENANT_ID,
42
+ now: nowIso
43
+ });
44
+ log("api client ready", { tenantId, schema: SCHEMA, jobs: JOBS, month: MONTH });
45
+
46
+ log("upserting finance account map");
47
+ const accountMap = await request({
48
+ method: "PUT",
49
+ path: "/ops/finance/account-map",
50
+ body: {
51
+ mapping: {
52
+ schemaVersion: "FinanceAccountMap.v1",
53
+ accounts: {
54
+ acct_cash: "1000",
55
+ acct_customer_escrow: "2100",
56
+ acct_platform_revenue: "4000",
57
+ acct_owner_payable: "2000",
58
+ acct_operator_payable: "2010",
59
+ acct_insurance_reserve: "2150",
60
+ acct_coverage_reserve: "2160",
61
+ acct_coverage_unearned: "2170",
62
+ acct_coverage_revenue: "4010",
63
+ acct_coverage_payout_expense: "5100",
64
+ acct_insurer_receivable: "1200",
65
+ acct_operator_chargeback_receivable: "1210",
66
+ acct_claims_expense: "5200",
67
+ acct_claims_payable: "2200",
68
+ acct_operator_labor_expense: "5300",
69
+ acct_operator_cost_accrued: "2210",
70
+ acct_developer_royalty_payable: "2020",
71
+ acct_sla_credits_expense: "4900",
72
+ acct_customer_credits_payable: "2110"
73
+ }
74
+ }
75
+ }
76
+ });
77
+ if (accountMap.statusCode !== 200) throw new Error(`ops finance account-map failed: ${accountMap.statusCode} ${accountMap.body}`);
78
+
79
+ log("setting month close hold policy to ALLOW_WITH_DISCLOSURE");
80
+ const govEventsRes = await request({
81
+ method: "GET",
82
+ path: "/ops/governance/events"
83
+ });
84
+ if (govEventsRes.statusCode !== 200) {
85
+ throw new Error(`governance events read failed: ${govEventsRes.statusCode} ${govEventsRes.body}`);
86
+ }
87
+ const govEvents = Array.isArray(govEventsRes.json?.events) ? govEventsRes.json.events : [];
88
+ const govPrevChainHash = govEvents.length
89
+ ? (typeof govEvents[govEvents.length - 1]?.chainHash === "string" ? govEvents[govEvents.length - 1].chainHash : null)
90
+ : null;
91
+ const govPolicyRes = await request({
92
+ method: "POST",
93
+ path: "/ops/governance/events",
94
+ headers: {
95
+ "x-proxy-expected-prev-chain-hash": govPrevChainHash ?? "null",
96
+ "x-idempotency-key": "backup_month_close_policy_allow_disclosure_v1"
97
+ },
98
+ body: {
99
+ type: "TENANT_POLICY_UPDATED",
100
+ scope: "tenant",
101
+ payload: {
102
+ effectiveFrom: `${MONTH}-01T00:00:00.000Z`,
103
+ policy: { finance: { monthCloseHoldPolicy: "ALLOW_WITH_DISCLOSURE" } },
104
+ reason: "backup-restore-drill"
105
+ }
106
+ }
107
+ });
108
+ if (govPolicyRes.statusCode !== 200 && govPolicyRes.statusCode !== 201) {
109
+ throw new Error(`month close policy update failed: ${govPolicyRes.statusCode} ${govPolicyRes.body}`);
110
+ }
111
+
112
+ log("registering backup robot");
113
+ const regRobot = await request({
114
+ method: "POST",
115
+ path: "/robots/register",
116
+ headers: { "x-idempotency-key": "backup_robot_reg_1" },
117
+ body: { robotId: "rob_backup", trustScore: 0.9, homeZoneId: "zone_a" }
118
+ });
119
+ if (regRobot.statusCode !== 201) throw new Error(`robot register failed: ${regRobot.statusCode} ${regRobot.body}`);
120
+ let robotPrev = regRobot.json?.robot?.lastChainHash ?? null;
121
+ if (!robotPrev) throw new Error("robot registration missing lastChainHash");
122
+
123
+ nowMs += 1_000;
124
+ log("setting robot availability");
125
+ const setAvail = await request({
126
+ method: "POST",
127
+ path: "/robots/rob_backup/availability",
128
+ headers: { "x-idempotency-key": "backup_robot_avail_1", "x-proxy-expected-prev-chain-hash": robotPrev },
129
+ body: { availability: [{ startAt: `${MONTH}-01T00:00:00.000Z`, endAt: `${MONTH}-28T23:59:59.999Z` }] }
130
+ });
131
+ if (setAvail.statusCode !== 201) throw new Error(`robot availability failed: ${setAvail.statusCode} ${setAvail.body}`);
132
+ robotPrev = setAvail.json?.robot?.lastChainHash ?? robotPrev;
133
+
134
+ const bookingStartAt = `${MONTH}-15T10:30:00.000Z`;
135
+ const bookingEndAt = `${MONTH}-15T11:00:00.000Z`;
136
+
137
+ const jobIds = [];
138
+
139
+ for (let i = 0; i < JOBS; i += 1) {
140
+ if (i === 0 || i === JOBS - 1 || i % 10 === 0) {
141
+ log("seeding jobs progress", { current: i + 1, total: JOBS });
142
+ }
143
+ const created = await request({
144
+ method: "POST",
145
+ path: "/jobs",
146
+ headers: { "x-idempotency-key": `backup_job_create_${i}` },
147
+ body: { templateId: "reset_lite", constraints: { zoneId: "zone_a" } }
148
+ });
149
+ if (created.statusCode !== 201) throw new Error(`job create failed: ${created.statusCode} ${created.body}`);
150
+
151
+ const jobId = created.json?.job?.id;
152
+ let prev = created.json?.job?.lastChainHash;
153
+ if (!jobId || !prev) throw new Error("job create did not return id/lastChainHash");
154
+ jobIds.push(jobId);
155
+
156
+ const postServerEvent = async (type, payload, idempotencyKey) => {
157
+ const res = await request({
158
+ method: "POST",
159
+ path: `/jobs/${jobId}/events`,
160
+ headers: { "x-proxy-expected-prev-chain-hash": prev, "x-idempotency-key": idempotencyKey },
161
+ body: { type, actor: { type: "system", id: "backup" }, payload }
162
+ });
163
+ if (res.statusCode !== 201) throw new Error(`post ${type} failed: ${res.statusCode} ${res.body}`);
164
+ prev = res.json?.job?.lastChainHash;
165
+ return res;
166
+ };
167
+
168
+ nowMs += 1_000;
169
+ await postServerEvent("QUOTE_PROPOSED", { amountCents: 6500 + i, currency: "USD" }, `backup_quote_${i}`);
170
+ nowMs += 1_000;
171
+ await postServerEvent(
172
+ "BOOKED",
173
+ makeBookedPayload({
174
+ paymentHoldId: `hold_backup_${jobId}`,
175
+ startAt: bookingStartAt,
176
+ endAt: bookingEndAt,
177
+ environmentTier: "ENV_MANAGED_BUILDING",
178
+ requiresOperatorCoverage: false,
179
+ customerId: "cust_backup",
180
+ siteId: "site_backup"
181
+ }),
182
+ `backup_book_${i}`
183
+ );
184
+ nowMs += 1_000;
185
+ await postServerEvent("MATCHED", { robotId: "rob_backup", operatorPartyId: "pty_operator_backup" }, `backup_match_${i}`);
186
+ nowMs += 1_000;
187
+ await postServerEvent("JOB_CANCELLED", { jobId, cancelledAt: nowIso(), reason: "OPS", requestedBy: "ops" }, `backup_cancel_${i}`);
188
+ nowMs += 1_000;
189
+ await postServerEvent("SETTLED", { settlement: "backup" }, `backup_settle_${i}`);
190
+ }
191
+
192
+ log("draining job accounting queue");
193
+ for (let i = 0; i < 50; i += 1) {
194
+ const tick = await api.tickJobAccounting({ maxMessages: 200 });
195
+ if (!Array.isArray(tick?.processed) || tick.processed.length === 0) break;
196
+ }
197
+ log("draining artifact queue");
198
+ for (let i = 0; i < 50; i += 1) {
199
+ const tick = await api.tickArtifacts({ maxMessages: 200 });
200
+ if (!Array.isArray(tick?.processed) || tick.processed.length === 0) break;
201
+ }
202
+
203
+ if (REQUIRE_MONTH_CLOSE) {
204
+ nowMs += 1_000;
205
+ log("requesting month close");
206
+ const closeReq = await request({ method: "POST", path: "/ops/month-close", body: { month: MONTH } });
207
+ if (closeReq.statusCode !== 200 && closeReq.statusCode !== 202) {
208
+ throw new Error(`month close request failed: ${closeReq.statusCode} ${closeReq.body}`);
209
+ }
210
+
211
+ let closed = false;
212
+ log("polling month close status");
213
+ for (let i = 0; i < 240; i += 1) {
214
+ const tick = await api.tickMonthClose({ maxMessages: 50 });
215
+ const failedTick = Array.isArray(tick?.processed) ? tick.processed.find((row) => row?.status === "failed") : null;
216
+ if (failedTick) {
217
+ throw new Error(`month close tick failed: ${JSON.stringify(failedTick)}`);
218
+ }
219
+ await api.tickArtifacts({ maxMessages: 200 });
220
+ const status = await request({ method: "GET", path: `/ops/month-close?month=${encodeURIComponent(MONTH)}` });
221
+ if (status.statusCode === 200 && status.json?.monthClose?.status === "CLOSED") {
222
+ closed = true;
223
+ break;
224
+ }
225
+ await new Promise((r) => setTimeout(r, 250));
226
+ }
227
+ if (!closed) throw new Error("month close did not reach CLOSED");
228
+ } else {
229
+ log("skipping month close request (set BACKUP_RESTORE_REQUIRE_MONTH_CLOSE=1 to enforce)");
230
+ }
231
+
232
+ await close();
233
+ log("seed workload complete");
234
+
235
+ process.stdout.write(JSON.stringify({ tenantId, jobIds, month: MONTH }, null, 2) + "\n");
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Verifies restored DB state matches expected digests.
3
+ */
4
+ import assert from "node:assert/strict";
5
+ import fs from "node:fs";
6
+
7
+ const expectedPath = process.argv[2];
8
+ if (!expectedPath) throw new Error("usage: node scripts/backup-restore/verify-state.mjs /path/to/expected.json");
9
+
10
+ const expected = JSON.parse(fs.readFileSync(expectedPath, "utf8"));
11
+
12
+ // Compute actual by reusing capture-state with the current env vars.
13
+ // We inline-import capture-state and intercept stdout by calling its logic directly.
14
+ import pg from "pg";
15
+ import { canonicalJsonStringify } from "../../src/core/canonical-json.js";
16
+ import { sha256Hex } from "../../src/core/crypto.js";
17
+ import { normalizeTenantId } from "../../src/core/tenancy.js";
18
+
19
+ const DATABASE_URL = process.env.DATABASE_URL;
20
+ if (!DATABASE_URL) throw new Error("DATABASE_URL is required");
21
+ const TENANT_ID = normalizeTenantId(process.env.TENANT_ID ?? expected?.tenantId ?? "tenant_default");
22
+ const SCHEMA = (() => {
23
+ const raw = String(process.env.PROXY_PG_SCHEMA ?? "public").trim();
24
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(raw)) {
25
+ throw new Error("PROXY_PG_SCHEMA must match [A-Za-z_][A-Za-z0-9_]*");
26
+ }
27
+ return raw;
28
+ })();
29
+
30
+ function digestRows(rows) {
31
+ return sha256Hex(canonicalJsonStringify(rows));
32
+ }
33
+
34
+ const { Client } = pg;
35
+ const client = new Client({ connectionString: DATABASE_URL });
36
+ await client.connect();
37
+ await client.query(`SET search_path TO "${SCHEMA}", public`);
38
+
39
+ const tenantCountRes = await client.query("SELECT COUNT(DISTINCT tenant_id)::int AS n FROM snapshots");
40
+ const jobCountRes = await client.query("SELECT COUNT(*)::int AS n FROM snapshots WHERE tenant_id = $1 AND aggregate_type = 'job'", [TENANT_ID]);
41
+ const ledgerEntryCountRes = await client.query("SELECT COUNT(*)::int AS n FROM ledger_entries WHERE tenant_id = $1", [TENANT_ID]);
42
+ const allocationCountRes = await client.query("SELECT COUNT(*)::int AS n FROM ledger_allocations WHERE tenant_id = $1", [TENANT_ID]);
43
+ const artifactCountRes = await client.query("SELECT COUNT(*)::int AS n FROM artifacts WHERE tenant_id = $1", [TENANT_ID]);
44
+ const partyStatementCountRes = await client.query("SELECT COUNT(*)::int AS n FROM party_statements WHERE tenant_id = $1", [TENANT_ID]);
45
+
46
+ const artifactsRes = await client.query(
47
+ `
48
+ SELECT artifact_id, artifact_type, artifact_hash
49
+ FROM artifacts
50
+ WHERE tenant_id = $1
51
+ AND artifact_type IN ('SettlementStatement.v1','MonthlyStatement.v1','PartyStatement.v1','PayoutInstruction.v1')
52
+ ORDER BY artifact_type ASC, artifact_id ASC
53
+ `,
54
+ [TENANT_ID]
55
+ );
56
+ const artifactsDigest = digestRows(
57
+ artifactsRes.rows.map((r) => ({
58
+ artifactId: String(r.artifact_id),
59
+ artifactType: String(r.artifact_type),
60
+ artifactHash: String(r.artifact_hash)
61
+ }))
62
+ );
63
+
64
+ const ledgerEntriesRes = await client.query(
65
+ `
66
+ SELECT entry_id, entry_json
67
+ FROM ledger_entries
68
+ WHERE tenant_id = $1
69
+ ORDER BY entry_id ASC
70
+ `,
71
+ [TENANT_ID]
72
+ );
73
+ const ledgerDigest = digestRows(
74
+ ledgerEntriesRes.rows.map((r) => ({
75
+ entryId: String(r.entry_id),
76
+ entry: r.entry_json ?? null
77
+ }))
78
+ );
79
+
80
+ const allocRes = await client.query(
81
+ `
82
+ SELECT entry_id, posting_id, account_id, party_id, party_role, currency, amount_cents
83
+ FROM ledger_allocations
84
+ WHERE tenant_id = $1
85
+ ORDER BY entry_id ASC, posting_id ASC, party_id ASC
86
+ `,
87
+ [TENANT_ID]
88
+ );
89
+ const allocationsDigest = digestRows(
90
+ allocRes.rows.map((r) => ({
91
+ entryId: String(r.entry_id),
92
+ postingId: String(r.posting_id),
93
+ accountId: r.account_id === null ? null : String(r.account_id),
94
+ partyId: String(r.party_id),
95
+ partyRole: String(r.party_role),
96
+ currency: String(r.currency),
97
+ amountCents: Number(r.amount_cents)
98
+ }))
99
+ );
100
+
101
+ const monthEventsRes = await client.query(
102
+ `
103
+ SELECT aggregate_id, seq, event_json
104
+ FROM events
105
+ WHERE tenant_id = $1 AND aggregate_type = 'month'
106
+ ORDER BY aggregate_id ASC, seq ASC
107
+ `,
108
+ [TENANT_ID]
109
+ );
110
+ const monthEventsDigest = digestRows(
111
+ monthEventsRes.rows.map((r) => ({
112
+ monthId: String(r.aggregate_id),
113
+ seq: Number(r.seq),
114
+ event: r.event_json ?? null
115
+ }))
116
+ );
117
+
118
+ await client.end();
119
+
120
+ const actual = {
121
+ tenantId: TENANT_ID,
122
+ counts: {
123
+ tenants: Number(tenantCountRes.rows?.[0]?.n ?? 0),
124
+ jobs: Number(jobCountRes.rows?.[0]?.n ?? 0),
125
+ ledgerEntries: Number(ledgerEntryCountRes.rows?.[0]?.n ?? 0),
126
+ allocations: Number(allocationCountRes.rows?.[0]?.n ?? 0),
127
+ artifacts: Number(artifactCountRes.rows?.[0]?.n ?? 0),
128
+ partyStatements: Number(partyStatementCountRes.rows?.[0]?.n ?? 0)
129
+ },
130
+ digests: {
131
+ artifacts: artifactsDigest,
132
+ ledgerEntries: ledgerDigest,
133
+ allocations: allocationsDigest,
134
+ monthEvents: monthEventsDigest
135
+ }
136
+ };
137
+
138
+ assert.deepEqual(actual, expected, "state mismatch after restore");
139
+ process.stdout.write("ok\n");
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ echo "=== Settld Backup/Restore Verification ==="
5
+
6
+ if [[ -z "${DATABASE_URL:-}" ]]; then
7
+ echo "DATABASE_URL is required" >&2
8
+ exit 2
9
+ fi
10
+ if [[ -z "${RESTORE_DATABASE_URL:-}" ]]; then
11
+ echo "RESTORE_DATABASE_URL is required" >&2
12
+ exit 2
13
+ fi
14
+
15
+ if ! command -v psql >/dev/null 2>&1; then
16
+ echo "psql is required on PATH" >&2
17
+ exit 2
18
+ fi
19
+
20
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
21
+ TMP="${TMPDIR:-/tmp}"
22
+ RUN_ID="settld_backup_restore_$(date +%s)"
23
+ SQL_DUMP="${TMP}/${RUN_ID}.sql"
24
+ EXPECTED_JSON="${TMP}/${RUN_ID}.expected.json"
25
+ STEP_TIMEOUT_SECONDS="${STEP_TIMEOUT_SECONDS:-600}"
26
+ VERIFY_FINANCE_PACK_RAW="${BACKUP_RESTORE_VERIFY_FINANCE_PACK:-0}"
27
+ VERIFY_FINANCE_PACK=0
28
+ case "$(printf '%s' "${VERIFY_FINANCE_PACK_RAW}" | tr '[:upper:]' '[:lower:]')" in
29
+ 1|true|yes) VERIFY_FINANCE_PACK=1 ;;
30
+ *) VERIFY_FINANCE_PACK=0 ;;
31
+ esac
32
+
33
+ export STORE=pg
34
+ export NODE_ENV=production
35
+ export PROXY_MIGRATE_ON_STARTUP=1
36
+
37
+ # Make the seed workload isolated by schema (so repeated runs don't collide).
38
+ # If the ambient env uses "public", force an isolated drill schema.
39
+ REQUESTED_SCHEMA="${PROXY_PG_SCHEMA:-}"
40
+ if [[ -z "${REQUESTED_SCHEMA}" || "${REQUESTED_SCHEMA}" == "public" ]]; then
41
+ export PROXY_PG_SCHEMA="backup_${RUN_ID}"
42
+ else
43
+ export PROXY_PG_SCHEMA="${REQUESTED_SCHEMA}"
44
+ fi
45
+ export TENANT_ID="${TENANT_ID:-tenant_default}"
46
+ export MONTH="${MONTH:-2026-01}"
47
+ export JOBS="${JOBS:-10}"
48
+ if [[ "${VERIFY_FINANCE_PACK}" -ne 1 ]]; then
49
+ export BACKUP_RESTORE_REQUIRE_MONTH_CLOSE=0
50
+ export BACKUP_RESTORE_REQUEST_MONTH_CLOSE=0
51
+ fi
52
+
53
+ SAME_DATABASE_URLS=0
54
+ if [[ "${DATABASE_URL}" == "${RESTORE_DATABASE_URL}" ]]; then
55
+ SAME_DATABASE_URLS=1
56
+ fi
57
+ RESTORE_SCHEMA="${PROXY_PG_RESTORE_SCHEMA:-${PROXY_PG_SCHEMA}_restore}"
58
+ if [[ "${#RESTORE_SCHEMA}" -gt 63 ]]; then
59
+ RESTORE_SCHEMA="${RESTORE_SCHEMA:0:63}"
60
+ fi
61
+ if [[ "${SAME_DATABASE_URLS}" -eq 1 && "${RESTORE_SCHEMA}" == "${PROXY_PG_SCHEMA}" ]]; then
62
+ RESTORE_SCHEMA="${PROXY_PG_SCHEMA}_r"
63
+ fi
64
+
65
+ step() {
66
+ local label="$1"
67
+ shift
68
+ local started
69
+ started="$(date +%s)"
70
+ echo "${label}"
71
+ if timeout "${STEP_TIMEOUT_SECONDS}" "$@"; then
72
+ local elapsed
73
+ elapsed="$(( $(date +%s) - started ))"
74
+ echo " -> ok (${elapsed}s)"
75
+ return 0
76
+ else
77
+ local code="$?"
78
+ local elapsed
79
+ elapsed="$(( $(date +%s) - started ))"
80
+ echo " -> failed (${elapsed}s, exit=${code})" >&2
81
+ return "${code}"
82
+ fi
83
+ }
84
+
85
+ dump_schema_to_file() {
86
+ local out_file="$1"
87
+ local err_file
88
+ err_file="$(mktemp "${TMP}/settld_pg_dump_err_${RUN_ID}_XXXX.txt")"
89
+
90
+ if command -v pg_dump >/dev/null 2>&1; then
91
+ if pg_dump --schema="${PROXY_PG_SCHEMA}" --no-owner --no-privileges "${DATABASE_URL}" > "${out_file}" 2>"${err_file}"; then
92
+ rm -f "${err_file}"
93
+ return 0
94
+ fi
95
+ if ! grep -qi "server version mismatch" "${err_file}"; then
96
+ cat "${err_file}" >&2
97
+ rm -f "${err_file}"
98
+ return 1
99
+ fi
100
+ fi
101
+
102
+ if ! command -v docker >/dev/null 2>&1; then
103
+ cat "${err_file}" >&2
104
+ rm -f "${err_file}"
105
+ return 1
106
+ fi
107
+
108
+ if ! docker run --rm postgres:17 pg_dump --schema="${PROXY_PG_SCHEMA}" --no-owner --no-privileges "${DATABASE_URL}" > "${out_file}" 2>"${err_file}"; then
109
+ cat "${err_file}" >&2
110
+ rm -f "${err_file}"
111
+ return 1
112
+ fi
113
+
114
+ rm -f "${err_file}"
115
+ return 0
116
+ }
117
+
118
+ clone_schema_in_place() {
119
+ local src_schema="$1"
120
+ local dst_schema="$2"
121
+ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<SQL >/dev/null
122
+ DROP SCHEMA IF EXISTS "${dst_schema}" CASCADE;
123
+ CREATE SCHEMA "${dst_schema}";
124
+ DO \$\$
125
+ DECLARE
126
+ t RECORD;
127
+ BEGIN
128
+ FOR t IN
129
+ SELECT tablename
130
+ FROM pg_tables
131
+ WHERE schemaname = '${src_schema}'
132
+ ORDER BY tablename
133
+ LOOP
134
+ EXECUTE format(
135
+ 'CREATE TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING GENERATED INCLUDING IDENTITY)',
136
+ '${dst_schema}',
137
+ t.tablename,
138
+ '${src_schema}',
139
+ t.tablename
140
+ );
141
+ EXECUTE format('INSERT INTO %I.%I SELECT * FROM %I.%I', '${dst_schema}', t.tablename, '${src_schema}', t.tablename);
142
+ END LOOP;
143
+ END
144
+ \$\$;
145
+ SQL
146
+ }
147
+
148
+ step "[1/8] Seed workload into source DB (schema=${PROXY_PG_SCHEMA})" \
149
+ node "${ROOT}/scripts/backup-restore/seed-workload.mjs"
150
+
151
+ step "[2/8] Capture expected state" \
152
+ bash -lc "node \"${ROOT}/scripts/backup-restore/capture-state.mjs\" > \"${EXPECTED_JSON}\""
153
+
154
+ FP_ZIP=""
155
+ FP_SHA=""
156
+ if [[ "${VERIFY_FINANCE_PACK}" -eq 1 ]]; then
157
+ echo "[3/8] Build FinancePackBundle and strict-verify (source DB)"
158
+ FINANCE_OUT="${TMP}/${RUN_ID}.finance_pack"
159
+ FP_ZIP="$(timeout "${STEP_TIMEOUT_SECONDS}" env DATABASE_URL="${DATABASE_URL}" TENANT_ID="${TENANT_ID}" PROXY_PG_SCHEMA="${PROXY_PG_SCHEMA}" node "${ROOT}/scripts/finance-pack/bundle.mjs" --period "${MONTH}" --out "${FINANCE_OUT}" --zip)"
160
+ step " strict verify source finance pack" \
161
+ node "${ROOT}/packages/artifact-verify/bin/settld-verify.js" --strict --finance-pack "${FP_ZIP}" >/dev/null
162
+ FP_SHA="$(node -e "import fs from 'node:fs'; import crypto from 'node:crypto'; const b=fs.readFileSync(process.argv[1]); console.log(crypto.createHash('sha256').update(b).digest('hex'))" "${FP_ZIP}")"
163
+ echo " -> ok ($(basename "${FP_ZIP}"))"
164
+ else
165
+ echo "[3/8] Skip FinancePackBundle strict verification (set BACKUP_RESTORE_VERIFY_FINANCE_PACK=1 to enable)"
166
+ fi
167
+
168
+ if [[ "${SAME_DATABASE_URLS}" -eq 1 ]]; then
169
+ echo "[4/8] Clone source schema (${PROXY_PG_SCHEMA}) -> (${RESTORE_SCHEMA})"
170
+ clone_started="$(date +%s)"
171
+ if clone_schema_in_place "${PROXY_PG_SCHEMA}" "${RESTORE_SCHEMA}"; then
172
+ echo " -> ok ($(( $(date +%s) - clone_started ))s)"
173
+ else
174
+ echo " -> failed ($(( $(date +%s) - clone_started ))s, exit=1)" >&2
175
+ exit 1
176
+ fi
177
+ echo "[5/8] Restore into restore DB (same DB URL; schema clone used)"
178
+ else
179
+ echo "[4/8] pg_dump source DB schema (${PROXY_PG_SCHEMA})"
180
+ dump_started="$(date +%s)"
181
+ if dump_schema_to_file "${SQL_DUMP}"; then
182
+ echo " -> ok ($(( $(date +%s) - dump_started ))s)"
183
+ else
184
+ echo " -> failed ($(( $(date +%s) - dump_started ))s, exit=1)" >&2
185
+ exit 1
186
+ fi
187
+
188
+ echo "[5/8] Restore into restore DB"
189
+ # Restore DB is assumed to exist and be empty-ish; caller can create it via CI setup.
190
+ step " probe restore DB connectivity" \
191
+ psql "${RESTORE_DATABASE_URL}" -v ON_ERROR_STOP=1 -c "SELECT 1" >/dev/null
192
+ step " restore SQL dump" \
193
+ psql "${RESTORE_DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${SQL_DUMP}" >/dev/null
194
+ fi
195
+
196
+ step "[6/8] Verify restored state" \
197
+ env DATABASE_URL="${RESTORE_DATABASE_URL}" PROXY_PG_SCHEMA="${RESTORE_SCHEMA}" node "${ROOT}/scripts/backup-restore/verify-state.mjs" "${EXPECTED_JSON}"
198
+
199
+ if [[ "${VERIFY_FINANCE_PACK}" -eq 1 ]]; then
200
+ echo "[7/8] Build FinancePackBundle and strict-verify (restored DB) + compare zip hash"
201
+ RESTORED_OUT="${TMP}/${RUN_ID}.finance_pack_restored"
202
+ FP_ZIP_RESTORED="$(timeout "${STEP_TIMEOUT_SECONDS}" env DATABASE_URL="${RESTORE_DATABASE_URL}" TENANT_ID="${TENANT_ID}" PROXY_PG_SCHEMA="${RESTORE_SCHEMA}" node "${ROOT}/scripts/finance-pack/bundle.mjs" --period "${MONTH}" --out "${RESTORED_OUT}" --zip)"
203
+ step " strict verify restored finance pack" \
204
+ node "${ROOT}/packages/artifact-verify/bin/settld-verify.js" --strict --finance-pack "${FP_ZIP_RESTORED}" >/dev/null
205
+ FP_SHA_RESTORED="$(node -e "import fs from 'node:fs'; import crypto from 'node:crypto'; const b=fs.readFileSync(process.argv[1]); console.log(crypto.createHash('sha256').update(b).digest('hex'))" "${FP_ZIP_RESTORED}")"
206
+ if [[ "${FP_SHA}" != "${FP_SHA_RESTORED}" ]]; then
207
+ echo "FinancePack zip hash mismatch after restore:" >&2
208
+ echo " source: ${FP_SHA} (${FP_ZIP})" >&2
209
+ echo " restored: ${FP_SHA_RESTORED} (${FP_ZIP_RESTORED})" >&2
210
+ exit 1
211
+ fi
212
+ else
213
+ echo "[7/8] Skip FinancePackBundle restore hash comparison (set BACKUP_RESTORE_VERIFY_FINANCE_PACK=1 to enable)"
214
+ fi
215
+
216
+ echo "[8/8] Done"
217
+ echo "=== Backup/Restore Verification PASSED ==="