settld 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +71 -0
- package/conformance/kernel-v0/README.md +7 -0
- package/conformance/kernel-v0/run.mjs +292 -4
- package/docs/ACCESS.md +57 -0
- package/docs/ADOPTION_CHECKLIST.md +44 -0
- package/docs/ALERTS.md +198 -0
- package/docs/ARCHITECTURE.md +69 -0
- package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
- package/docs/ARTIFACTS.md +60 -0
- package/docs/CERTIFICATION_CHECKLIST.md +33 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
- package/docs/CONFIG.md +297 -0
- package/docs/CONTRACTS_APIS.md +23 -0
- package/docs/DEPRECATION.md +31 -0
- package/docs/DOMAIN_MODEL.md +92 -0
- package/docs/EVENT_ENVELOPE.md +53 -0
- package/docs/FINANCE_PACK_FORMAT.md +53 -0
- package/docs/INCIDENT_TAXONOMY.md +30 -0
- package/docs/JOB_STATE_MACHINE.md +66 -0
- package/docs/KERNEL_COMPATIBLE.md +60 -0
- package/docs/KERNEL_V0.md +40 -0
- package/docs/KEY_ROTATION.md +80 -0
- package/docs/LEDGER.md +82 -0
- package/docs/LIVENESS.md +76 -0
- package/docs/MVP_BUILD_ORDER.md +36 -0
- package/docs/ONCALL_PLAYBOOK.md +39 -0
- package/docs/OPERATIONS_SIGNING.md +20 -0
- package/docs/OVERVIEW.md +190 -0
- package/docs/PERF_BASELINE.md +85 -0
- package/docs/PRD.md +77 -0
- package/docs/QUICKSTART_KERNEL_V0.md +96 -0
- package/docs/QUICKSTART_MCP.md +377 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
- package/docs/QUICKSTART_SDK.md +125 -0
- package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
- package/docs/QUICKSTART_VERIFY.md +54 -0
- package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
- package/docs/README.md +33 -0
- package/docs/RELEASE_CHECKLIST.md +182 -0
- package/docs/RELEASING.md +82 -0
- package/docs/REPO_SETTINGS.md +37 -0
- package/docs/RUNBOOK.md +86 -0
- package/docs/SKILLS.md +42 -0
- package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
- package/docs/SLO.md +131 -0
- package/docs/SUMMARY.md +17 -0
- package/docs/SUPPORT.md +31 -0
- package/docs/THREAT_MODEL.md +36 -0
- package/docs/TRUST.md +59 -0
- package/docs/WORKFLOW.md +35 -0
- package/docs/X402_BATCH_SETTLEMENT.md +126 -0
- package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
- package/docs/examples/x402-provider-payout-registry.example.json +14 -0
- package/docs/gitbook/README.md +64 -0
- package/docs/gitbook/SETUP.md +25 -0
- package/docs/gitbook/SUMMARY.md +15 -0
- package/docs/gitbook/api-reference.md +73 -0
- package/docs/gitbook/closepacks.md +55 -0
- package/docs/gitbook/conformance.md +59 -0
- package/docs/gitbook/core-primitives.md +85 -0
- package/docs/gitbook/dispute-lifecycle.md +33 -0
- package/docs/gitbook/faq.md +21 -0
- package/docs/gitbook/guides.md +49 -0
- package/docs/gitbook/operations-runbook.md +36 -0
- package/docs/gitbook/quickstart.md +103 -0
- package/docs/gitbook/replay-and-audit.md +30 -0
- package/docs/gitbook/sdk-reference.md +35 -0
- package/docs/gitbook/security-model.md +58 -0
- package/docs/integrations/README.md +15 -0
- package/docs/integrations/github-actions-verify.yml +31 -0
- package/docs/integrations/github-actions.md +34 -0
- package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
- package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
- package/docs/kernel-compatible/capabilities.json +36 -0
- package/docs/marketing/agent-commerce-substrate.md +78 -0
- package/docs/marketing/hn-repost-2026-02-17.md +102 -0
- package/docs/marketing/show-hn-post.md +45 -0
- package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
- package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
- package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
- package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
- package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
- package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
- package/docs/ops/R1_SLOS.md +66 -0
- package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
- package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
- package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
- package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
- package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
- package/docs/ops/tool-call-disputes-holdback.md +52 -0
- package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
- package/docs/pilot-kit/README.md +29 -0
- package/docs/pilot-kit/architecture-one-pager.md +48 -0
- package/docs/pilot-kit/buyer-email.txt +19 -0
- package/docs/pilot-kit/buyer-one-pager.md +31 -0
- package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
- package/docs/pilot-kit/offline-verify.md +33 -0
- package/docs/pilot-kit/procurement-one-pager.md +50 -0
- package/docs/pilot-kit/rfp-clause.md +46 -0
- package/docs/pilot-kit/roi-calculator-template.csv +2 -0
- package/docs/pilot-kit/security-qa.md +153 -0
- package/docs/pilot-kit/security-summary.md +35 -0
- package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/AcceptanceCriteria.v1.md +17 -0
- package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
- package/docs/spec/AgentEvent.v1.md +47 -0
- package/docs/spec/AgentIdentity.v1.md +62 -0
- package/docs/spec/AgentPassport.v1.md +95 -0
- package/docs/spec/AgentReputation.v1.md +59 -0
- package/docs/spec/AgentReputation.v2.md +52 -0
- package/docs/spec/AgentRun.v1.md +47 -0
- package/docs/spec/AgentRunSettlement.v1.md +52 -0
- package/docs/spec/AgentWallet.v1.md +43 -0
- package/docs/spec/AgreementDelegation.v1.md +109 -0
- package/docs/spec/ArbitrationCase.v1.md +67 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/ArbitrationVerdict.v1.md +60 -0
- package/docs/spec/BundleHeadAttestation.v1.md +32 -0
- package/docs/spec/CANONICAL_JSON.md +31 -0
- package/docs/spec/CRYPTOGRAPHY.md +61 -0
- package/docs/spec/ClosePack.v1.md +49 -0
- package/docs/spec/ClosePackManifest.v1.md +24 -0
- package/docs/spec/DelegationGrant.v1.md +90 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
- package/docs/spec/ERRORS.md +76 -0
- package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
- package/docs/spec/EvidenceIndex.v1.md +20 -0
- package/docs/spec/ExecutionIntent.v1.md +90 -0
- package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
- package/docs/spec/FundingHold.v1.md +60 -0
- package/docs/spec/GovernancePolicy.v1.md +34 -0
- package/docs/spec/GovernancePolicy.v2.md +30 -0
- package/docs/spec/INVARIANTS.md +389 -0
- package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
- package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
- package/docs/spec/InvoiceClaim.v1.md +11 -0
- package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
- package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
- package/docs/spec/MarketplaceOffer.v2.md +54 -0
- package/docs/spec/MeteringReport.v1.md +18 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PRODUCER_ERRORS.md +42 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/PricingMatrix.v1.md +20 -0
- package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
- package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
- package/docs/spec/ProduceCliOutput.v1.md +46 -0
- package/docs/spec/ProofBundleManifest.v1.md +24 -0
- package/docs/spec/README.md +109 -0
- package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
- package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
- package/docs/spec/REMOTE_SIGNER.md +66 -0
- package/docs/spec/ReleaseIndex.v1.md +32 -0
- package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
- package/docs/spec/ReleaseTrust.v1.md +13 -0
- package/docs/spec/ReleaseTrust.v2.md +26 -0
- package/docs/spec/RemoteSignerRequest.v1.md +21 -0
- package/docs/spec/RemoteSignerResponse.v1.md +16 -0
- package/docs/spec/ReputationEvent.v1.md +63 -0
- package/docs/spec/RevocationList.v1.md +28 -0
- package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
- package/docs/spec/STRICTNESS.md +68 -0
- package/docs/spec/SUPPLY_CHAIN.md +33 -0
- package/docs/spec/SettlementAdjustment.v1.md +45 -0
- package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
- package/docs/spec/SettlementDecisionReport.v1.md +44 -0
- package/docs/spec/SettlementKernel.v1.md +59 -0
- package/docs/spec/SettlementReceipt.v1.md +63 -0
- package/docs/spec/SlaDefinition.v1.md +24 -0
- package/docs/spec/SlaEvaluation.v1.md +12 -0
- package/docs/spec/THREAT_MODEL.md +113 -0
- package/docs/spec/TOOL_PROVENANCE.md +30 -0
- package/docs/spec/TRUST_ANCHORS.md +84 -0
- package/docs/spec/TenantSettings.v1.md +90 -0
- package/docs/spec/TenantSettings.v2.md +99 -0
- package/docs/spec/TimestampProof.v1.md +25 -0
- package/docs/spec/ToolCallAgreement.v1.md +34 -0
- package/docs/spec/ToolCallEvidence.v1.md +47 -0
- package/docs/spec/ToolManifest.v1.md +47 -0
- package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
- package/docs/spec/VERSIONING.md +107 -0
- package/docs/spec/VerificationReport.v1.md +50 -0
- package/docs/spec/VerifyAboutOutput.v1.md +10 -0
- package/docs/spec/VerifyCliOutput.v1.md +28 -0
- package/docs/spec/WARNINGS.md +83 -0
- package/docs/spec/error-codes.v1.txt +285 -0
- package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
- package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
- package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
- package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
- package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
- package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
- package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
- package/docs/spec/examples/release_index_v1.example.json +15 -0
- package/docs/spec/examples/release_trust_v1.example.json +7 -0
- package/docs/spec/examples/release_trust_v2.example.json +22 -0
- package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
- package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
- package/docs/spec/examples/reputation_event_v1.example.json +29 -0
- package/docs/spec/examples/verification_report_v1.example.json +24 -0
- package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
- package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
- package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
- package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
- package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
- package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
- package/docs/spec/producer-error-codes.v1.txt +14 -0
- package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
- package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
- package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
- package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
- package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
- package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
- package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
- package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
- package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
- package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
- package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
- package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
- package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
- package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
- package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
- package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
- package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
- package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
- package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
- package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
- package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
- package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
- package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
- package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
- package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
- package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
- package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
- package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
- package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
- package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
- package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
- package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
- package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
- package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
- package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
- package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
- package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
- package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
- package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
- package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
- package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
- package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
- package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
- package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
- package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
- package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
- package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
- package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
- package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
- package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
- package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
- package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
- package/docs/spec/x402-error-codes.v1.txt +35 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +53 -6
- package/scripts/acceptance/full-stack.mjs +734 -0
- package/scripts/acceptance/full-stack.sh +99 -0
- package/scripts/audit/build-audit-packet.mjs +242 -0
- package/scripts/backup-pg.sh +45 -0
- package/scripts/backup-restore/README.md +18 -0
- package/scripts/backup-restore/capture-state.mjs +130 -0
- package/scripts/backup-restore/client.mjs +97 -0
- package/scripts/backup-restore/seed-workload.mjs +235 -0
- package/scripts/backup-restore/verify-state.mjs +139 -0
- package/scripts/backup-restore-test.sh +217 -0
- package/scripts/chaos.js +221 -0
- package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
- package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
- package/scripts/ci/changelog-guard.mjs +145 -0
- package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
- package/scripts/ci/check-secret-hygiene.mjs +78 -0
- package/scripts/ci/check-version-consistency.mjs +42 -0
- package/scripts/ci/cli-pack-smoke.mjs +160 -0
- package/scripts/ci/flake-budget-guard.mjs +68 -0
- package/scripts/ci/generate-error-codes.mjs +54 -0
- package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
- package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
- package/scripts/ci/npm-pack-smoke.mjs +454 -0
- package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
- package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
- package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
- package/scripts/ci/run-go-live-gate.mjs +150 -0
- package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
- package/scripts/ci/runtime-import-smoke.mjs +58 -0
- package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
- package/scripts/closepack/lib.mjs +286 -0
- package/scripts/collect-debug.sh +263 -0
- package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
- package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
- package/scripts/demo/delivery-robot/generate.mjs +377 -0
- package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
- package/scripts/demo/magic-link-first-green.mjs +118 -0
- package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
- package/scripts/demo/mcp-paid-exa.mjs +1110 -0
- package/scripts/dev/billing-doctor.sh +145 -0
- package/scripts/dev/billing-smoke-prod.sh +219 -0
- package/scripts/dev/billing-webhook-replay.sh +161 -0
- package/scripts/dev/env.dev.example +29 -0
- package/scripts/dev/env.sh +37 -0
- package/scripts/dev/new-sdk-key.sh +81 -0
- package/scripts/dev/sdk-first-run.sh +21 -0
- package/scripts/dev/smoke-x402-gateway.sh +115 -0
- package/scripts/dev/start-api.sh +24 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
- package/scripts/examples/sdk-first-paid-rfq.py +105 -0
- package/scripts/examples/sdk-first-verified-run.mjs +85 -0
- package/scripts/examples/sdk-first-verified-run.py +99 -0
- package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
- package/scripts/examples/sdk-tenant-analytics.py +118 -0
- package/scripts/finance-pack/bundle.mjs +284 -0
- package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
- package/scripts/governance/export.mjs +169 -0
- package/scripts/load/delivery-stress.k6.js +183 -0
- package/scripts/load/ingest-burst.k6.js +236 -0
- package/scripts/load/run-delivery-load.js +66 -0
- package/scripts/load/webhook-receiver.js +131 -0
- package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
- package/scripts/mcp/probe.mjs +238 -0
- package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
- package/scripts/mcp/settld-mcp-server.mjs +1511 -0
- package/scripts/openapi/write.mjs +13 -0
- package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
- package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
- package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
- package/scripts/ops/design-partner-run-packet.mjs +466 -0
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
- package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
- package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
- package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/pilot/finance-pack.mjs +495 -0
- package/scripts/pilot/fixtures/robot-keypair.json +4 -0
- package/scripts/pilot/fixtures/server-signer.json +4 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/proof-bundle/job.mjs +109 -0
- package/scripts/proof-bundle/lib.mjs +92 -0
- package/scripts/proof-bundle/month.mjs +103 -0
- package/scripts/provider/conformance-run.mjs +159 -0
- package/scripts/provider/keys-generate.mjs +135 -0
- package/scripts/provider/publish.mjs +420 -0
- package/scripts/quickstart/x402.mjs +334 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/release/build-artifacts.mjs +181 -0
- package/scripts/release/generate-release-index.mjs +112 -0
- package/scripts/release/release-index-lib.mjs +232 -0
- package/scripts/release/sign-release-index.mjs +85 -0
- package/scripts/release/validate-release-assets.mjs +170 -0
- package/scripts/release/verify-release.mjs +261 -0
- package/scripts/restore-pg.sh +34 -0
- package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
- package/scripts/sdk/smoke-python.py +30 -0
- package/scripts/sdk/smoke.mjs +16 -0
- package/scripts/settlement/x402-batch-worker.mjs +1091 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +239 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +59 -0
- package/scripts/trust/validate-trust-file.mjs +57 -0
- package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
- package/scripts/trust-config/wizard.mjs +161 -0
- package/scripts/vendor-contract-test-lib.mjs +182 -0
- package/scripts/vendor-contract-test.mjs +55 -0
- package/scripts/vercel/build-mkdocs.sh +9 -0
- package/scripts/vercel/ignore-mkdocs.sh +25 -0
- package/scripts/vercel/install-mkdocs.sh +6 -0
- package/scripts/verify-pg.js +217 -0
- package/scripts/x402/receipt-verify.mjs +289 -0
- package/services/finance-sink/src/dedupe-store.js +29 -6
- package/services/receiver/src/dedupe-store.js +29 -5
- package/services/x402-gateway/Dockerfile +13 -0
- package/services/x402-gateway/README.md +58 -0
- package/services/x402-gateway/examples/upstream-mock.js +337 -0
- package/services/x402-gateway/src/server.js +1058 -0
- package/src/api/app.js +34658 -16940
- package/src/api/maintenance.js +70 -0
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +1778 -70
- package/src/api/persistence.js +456 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1581 -62
- package/src/api/workers/deliveries.js +99 -4
- package/src/api/workers/insolvency-sweep.js +159 -0
- package/src/core/agent-card.js +69 -0
- package/src/core/agent-wallets.js +231 -0
- package/src/core/agreement-delegation.js +549 -0
- package/src/core/billing-plans.js +40 -6
- package/src/core/circle-reserve-adapter.js +845 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/maintenance-locks.js +1 -0
- package/src/core/operator-action.js +303 -0
- package/src/core/paid-tool-manifest.js +318 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/provider-publish-conformance.js +525 -0
- package/src/core/provider-publish-proof.js +396 -0
- package/src/core/provider-quote-signature.js +170 -0
- package/src/core/settld-keys.js +112 -0
- package/src/core/settld-pay-token.js +344 -0
- package/src/core/settlement-kernel.js +239 -2
- package/src/core/settlement-verifier.js +335 -0
- package/src/core/tool-call-agreement.js +112 -0
- package/src/core/tool-call-evidence.js +144 -0
- package/src/core/tool-provider-signature.js +98 -0
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/core/x402-escalation-override.js +258 -0
- package/src/core/x402-gate.js +118 -0
- package/src/core/x402-provider-refund-decision.js +220 -0
- package/src/core/x402-receipt-verifier.js +708 -0
- package/src/core/x402-reversal-command.js +251 -0
- package/src/core/x402-wallet-issuer-decision.js +252 -0
- package/src/core/zk-verifier.js +300 -0
- package/src/db/migrations/029_reputation_event_index.sql +54 -0
- package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
- package/src/db/pg.js +18 -7
- package/src/db/store-pg.js +1508 -111
|
@@ -0,0 +1,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 ==="
|