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,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
+
cd "$ROOT_DIR"
|
|
6
|
+
|
|
7
|
+
ACCEPTANCE_CLEAN="${ACCEPTANCE_CLEAN:-1}"
|
|
8
|
+
ACCEPTANCE_PROFILE="${ACCEPTANCE_PROFILE:-app}"
|
|
9
|
+
|
|
10
|
+
if ! docker info >/dev/null 2>&1; then
|
|
11
|
+
echo "acceptance: docker is not available (is the daemon running, and do you have socket access?)" >&2
|
|
12
|
+
exit 2
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
RUN_ID="${ACCEPTANCE_RUN_ID:-$(date +%s)_$RANDOM}"
|
|
16
|
+
PROJECT="${ACCEPTANCE_PROJECT_NAME:-settld_acc_${RUN_ID}}"
|
|
17
|
+
SCHEMA="${ACCEPTANCE_PG_SCHEMA:-acc_${RUN_ID}}"
|
|
18
|
+
ART_DIR="${ACCEPTANCE_ARTIFACT_DIR:-$(mktemp -d)}"
|
|
19
|
+
|
|
20
|
+
pick_port() {
|
|
21
|
+
node --input-type=module -e "import net from 'node:net'; const s=net.createServer(); s.listen(0,'127.0.0.1',()=>{console.log(s.address().port); s.close();});"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
API_PORT="${ACCEPTANCE_API_PORT:-$(pick_port)}"
|
|
25
|
+
RECEIVER_PORT="${ACCEPTANCE_RECEIVER_PORT:-$(pick_port)}"
|
|
26
|
+
MINIO_PORT="${ACCEPTANCE_MINIO_PORT:-$(pick_port)}"
|
|
27
|
+
MINIO_CONSOLE_PORT="${ACCEPTANCE_MINIO_CONSOLE_PORT:-$(pick_port)}"
|
|
28
|
+
|
|
29
|
+
export COMPOSE_PROJECT_NAME="${PROJECT}"
|
|
30
|
+
export PROXY_PG_SCHEMA="${SCHEMA}"
|
|
31
|
+
export PROXY_API_PORT="${API_PORT}"
|
|
32
|
+
export PROXY_RECEIVER_PORT="${RECEIVER_PORT}"
|
|
33
|
+
export PROXY_MINIO_PORT="${MINIO_PORT}"
|
|
34
|
+
export PROXY_MINIO_CONSOLE_PORT="${MINIO_CONSOLE_PORT}"
|
|
35
|
+
export ACCEPTANCE_ARTIFACT_DIR="${ART_DIR}"
|
|
36
|
+
export ACCEPTANCE_API_BASE_URL="${ACCEPTANCE_API_BASE_URL:-http://127.0.0.1:${API_PORT}}"
|
|
37
|
+
export ACCEPTANCE_RECEIVER_BASE_URL="${ACCEPTANCE_RECEIVER_BASE_URL:-http://127.0.0.1:${RECEIVER_PORT}}"
|
|
38
|
+
export ACCEPTANCE_MINIO_ENDPOINT="${ACCEPTANCE_MINIO_ENDPOINT:-http://127.0.0.1:${MINIO_PORT}}"
|
|
39
|
+
|
|
40
|
+
mkdir -p "${ART_DIR}"
|
|
41
|
+
echo "acceptance: artifacts dir: ${ART_DIR}"
|
|
42
|
+
echo "acceptance: COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}"
|
|
43
|
+
echo "acceptance: PROXY_PG_SCHEMA=${PROXY_PG_SCHEMA}"
|
|
44
|
+
echo "acceptance: PROXY_API_PORT=${PROXY_API_PORT}"
|
|
45
|
+
echo "acceptance: PROXY_RECEIVER_PORT=${PROXY_RECEIVER_PORT}"
|
|
46
|
+
echo "acceptance: PROXY_MINIO_PORT=${PROXY_MINIO_PORT}"
|
|
47
|
+
echo "acceptance: PROXY_MINIO_CONSOLE_PORT=${PROXY_MINIO_CONSOLE_PORT}"
|
|
48
|
+
echo "acceptance: ACCEPTANCE_API_BASE_URL=${ACCEPTANCE_API_BASE_URL}"
|
|
49
|
+
echo "acceptance: ACCEPTANCE_RECEIVER_BASE_URL=${ACCEPTANCE_RECEIVER_BASE_URL}"
|
|
50
|
+
echo "acceptance: ACCEPTANCE_MINIO_ENDPOINT=${ACCEPTANCE_MINIO_ENDPOINT}"
|
|
51
|
+
|
|
52
|
+
compose_down() {
|
|
53
|
+
local profile_args=(--profile "${ACCEPTANCE_PROFILE}")
|
|
54
|
+
local exit_code="${1:-0}"
|
|
55
|
+
if [[ "${exit_code}" != "0" ]]; then
|
|
56
|
+
echo "acceptance: failure diagnostics"
|
|
57
|
+
docker compose "${profile_args[@]}" ps || true
|
|
58
|
+
docker compose "${profile_args[@]}" logs --no-color --tail=400 >"${ART_DIR}/compose.logs.tail.txt" 2>&1 || true
|
|
59
|
+
cat "${ART_DIR}/compose.logs.tail.txt" || true
|
|
60
|
+
|
|
61
|
+
# Best-effort: if context exists, snapshot key ops endpoints for debugging.
|
|
62
|
+
if [[ -f "${ART_DIR}/context.json" ]]; then
|
|
63
|
+
node --input-type=module -e "import fs from 'node:fs'; const c=JSON.parse(fs.readFileSync(process.env.ACCEPTANCE_ARTIFACT_DIR + '/context.json','utf8')); console.log(c?.bearer ?? '');" \
|
|
64
|
+
>"${ART_DIR}/bearer.txt" 2>/dev/null || true
|
|
65
|
+
BEARER="$(cat "${ART_DIR}/bearer.txt" 2>/dev/null || true)"
|
|
66
|
+
if [[ -n "${BEARER}" ]]; then
|
|
67
|
+
BEARER="${BEARER}" TENANT="${ACCEPTANCE_TENANT_ID:-tenant_default}" PROTO="${ACCEPTANCE_PROTOCOL:-1.0}" API="${ACCEPTANCE_API_BASE_URL:-http://127.0.0.1:3000}" \
|
|
68
|
+
node --input-type=module -e "const b=process.env.BEARER; const t=process.env.TENANT; const p=process.env.PROTO; const base=process.env.API; async function get(path){const r=await fetch(base+path,{headers:{authorization:b,'x-proxy-tenant-id':t,'x-settld-protocol':p}}).catch(()=>null); if(!r){console.log(path, 'fetch_failed'); return;} const txt=await r.text(); console.log(path, r.status); console.log(txt.slice(0, 4000));} await get('/ops/status'); await get('/ops/deliveries?limit=50'); await get('/ops/dlq?type=delivery&limit=50');" \
|
|
69
|
+
>"${ART_DIR}/ops.snapshots.txt" 2>&1 || true
|
|
70
|
+
cat "${ART_DIR}/ops.snapshots.txt" || true
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [[ "${ACCEPTANCE_CLEAN}" == "1" ]]; then
|
|
76
|
+
docker compose --profile "${ACCEPTANCE_PROFILE}" down -v --remove-orphans >/dev/null 2>&1 || true
|
|
77
|
+
else
|
|
78
|
+
docker compose --profile "${ACCEPTANCE_PROFILE}" down --remove-orphans >/dev/null 2>&1 || true
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
cleanup() {
|
|
83
|
+
local code="$?"
|
|
84
|
+
compose_down "${code}"
|
|
85
|
+
}
|
|
86
|
+
trap cleanup EXIT
|
|
87
|
+
|
|
88
|
+
compose_down 0
|
|
89
|
+
|
|
90
|
+
echo "acceptance: starting docker compose (profile=${ACCEPTANCE_PROFILE})"
|
|
91
|
+
docker compose --profile "${ACCEPTANCE_PROFILE}" up -d --build
|
|
92
|
+
|
|
93
|
+
echo "acceptance: seeding minio buckets"
|
|
94
|
+
docker compose --profile init run --rm minio-init
|
|
95
|
+
|
|
96
|
+
echo "acceptance: running full-stack checks"
|
|
97
|
+
node scripts/acceptance/full-stack.mjs
|
|
98
|
+
|
|
99
|
+
echo "acceptance: OK"
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
function sh(cmd, args, { cwd, env, stdin } = {}) {
|
|
7
|
+
const res = spawnSync(cmd, args, { cwd, env, input: stdin, encoding: "utf8" });
|
|
8
|
+
if (res.status !== 0) {
|
|
9
|
+
const err = (res.stderr || res.stdout || "").trim();
|
|
10
|
+
throw new Error(`${cmd} ${args.join(" ")} failed (exit ${res.status})${err ? `: ${err}` : ""}`);
|
|
11
|
+
}
|
|
12
|
+
return res.stdout;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function sha256FileHex(fp) {
|
|
16
|
+
const h = crypto.createHash("sha256");
|
|
17
|
+
const f = await fs.open(fp, "r");
|
|
18
|
+
try {
|
|
19
|
+
const buf = Buffer.alloc(1024 * 1024);
|
|
20
|
+
while (true) {
|
|
21
|
+
// eslint-disable-next-line no-await-in-loop
|
|
22
|
+
const { bytesRead } = await f.read(buf, 0, buf.length, null);
|
|
23
|
+
if (bytesRead === 0) break;
|
|
24
|
+
h.update(buf.subarray(0, bytesRead));
|
|
25
|
+
}
|
|
26
|
+
} finally {
|
|
27
|
+
await f.close();
|
|
28
|
+
}
|
|
29
|
+
return h.digest("hex");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseArgs(argv) {
|
|
33
|
+
let outDir = path.resolve(process.cwd(), "dist", "audit");
|
|
34
|
+
let version = "v1";
|
|
35
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
36
|
+
const a = argv[i];
|
|
37
|
+
if (a === "--out") {
|
|
38
|
+
outDir = path.resolve(process.cwd(), String(argv[i + 1] ?? ""));
|
|
39
|
+
i += 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (a === "--packet-version") {
|
|
43
|
+
version = String(argv[i + 1] ?? "v1");
|
|
44
|
+
i += 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (a === "--help" || a === "-h") {
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.error("usage: node scripts/audit/build-audit-packet.mjs [--out <dir>] [--packet-version v1]");
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.error(`unknown arg: ${a}`);
|
|
54
|
+
process.exit(2);
|
|
55
|
+
}
|
|
56
|
+
return { outDir, version };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function listFilesRecursive(root) {
|
|
60
|
+
const out = [];
|
|
61
|
+
async function walk(cur) {
|
|
62
|
+
const entries = await fs.readdir(cur, { withFileTypes: true });
|
|
63
|
+
for (const e of entries) {
|
|
64
|
+
const fp = path.join(cur, e.name);
|
|
65
|
+
if (e.isDirectory()) {
|
|
66
|
+
// eslint-disable-next-line no-await-in-loop
|
|
67
|
+
await walk(fp);
|
|
68
|
+
} else if (e.isFile()) out.push(fp);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
await walk(root);
|
|
72
|
+
out.sort();
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function writeReadme({ dst, packetName, tool }) {
|
|
77
|
+
const text = `# Settld Audit Packet (${packetName})
|
|
78
|
+
|
|
79
|
+
This archive is intended to be a self-contained “evidence bundle” for a hostile/skeptical reader.
|
|
80
|
+
|
|
81
|
+
## Contents
|
|
82
|
+
|
|
83
|
+
- \`spec/\` — protocol specs (including threat model + invariants checklist)
|
|
84
|
+
- \`protocol-vectors/\` — canonical protocol vectors
|
|
85
|
+
- \`conformance/\` — the conformance pack tarball + checksums
|
|
86
|
+
- \`tool.json\` — tool provenance summary for the build that produced this packet
|
|
87
|
+
- \`SHA256SUMS\` — checksums for files in this packet
|
|
88
|
+
|
|
89
|
+
## How to validate (minimum)
|
|
90
|
+
|
|
91
|
+
1. Verify checksums (example):
|
|
92
|
+
|
|
93
|
+
\`\`\`sh
|
|
94
|
+
(cd ${packetName} && sha256sum -c SHA256SUMS)
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
2. Install the released verifier CLI (\`settld-verify\`).
|
|
98
|
+
|
|
99
|
+
- If published to npm, install a pinned version.
|
|
100
|
+
- Otherwise, download the release npm tarball for \`settld-artifact-verify\` and install it locally.
|
|
101
|
+
|
|
102
|
+
3. Run conformance pack (requires \`settld-verify\` in PATH):
|
|
103
|
+
|
|
104
|
+
\`\`\`sh
|
|
105
|
+
tar -xzf conformance/conformance-v1.tar.gz
|
|
106
|
+
node conformance-v1/run.mjs
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
## Tool provenance
|
|
110
|
+
|
|
111
|
+
This packet was built from:
|
|
112
|
+
|
|
113
|
+
\`\`\`json
|
|
114
|
+
${JSON.stringify(tool, null, 2)}
|
|
115
|
+
\`\`\`
|
|
116
|
+
`;
|
|
117
|
+
await fs.writeFile(dst, text, "utf8");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function main() {
|
|
121
|
+
const { outDir, version } = parseArgs(process.argv.slice(2));
|
|
122
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
123
|
+
|
|
124
|
+
const repoRoot = process.cwd();
|
|
125
|
+
|
|
126
|
+
const toolVersion = (() => {
|
|
127
|
+
try {
|
|
128
|
+
const v = String((sh(process.execPath, ["-e", "const fs=require('fs'); console.log(String(fs.readFileSync('SETTLD_VERSION','utf8')).trim())"]).trim()) ?? "");
|
|
129
|
+
return v || null;
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
})();
|
|
134
|
+
const toolCommit = (() => {
|
|
135
|
+
try {
|
|
136
|
+
const v = sh("git", ["rev-parse", "HEAD"], { cwd: repoRoot }).trim();
|
|
137
|
+
return v || null;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
|
|
143
|
+
const packetName = `settld-audit-packet-${version}`;
|
|
144
|
+
const stagingRoot = await fs.mkdtemp(path.join(outDir, `${packetName}-staging-`));
|
|
145
|
+
const packetRoot = path.join(stagingRoot, packetName);
|
|
146
|
+
await fs.mkdir(packetRoot, { recursive: true });
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// 1) Copy specs (docs/spec/*)
|
|
150
|
+
await fs.cp(path.join(repoRoot, "docs", "spec"), path.join(packetRoot, "spec"), { recursive: true });
|
|
151
|
+
|
|
152
|
+
// 2) Copy protocol vectors
|
|
153
|
+
await fs.mkdir(path.join(packetRoot, "protocol-vectors"), { recursive: true });
|
|
154
|
+
await fs.copyFile(
|
|
155
|
+
path.join(repoRoot, "test", "fixtures", "protocol-vectors", "v1.json"),
|
|
156
|
+
path.join(packetRoot, "protocol-vectors", "v1.json")
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// 3) Build conformance pack tarball and checksum
|
|
160
|
+
const conformanceOutDir = path.join(packetRoot, "conformance");
|
|
161
|
+
await fs.mkdir(conformanceOutDir, { recursive: true });
|
|
162
|
+
await fs.rm(path.join(stagingRoot, "conformance-v1"), { recursive: true, force: true });
|
|
163
|
+
await fs.mkdir(path.join(stagingRoot, "conformance-v1"), { recursive: true });
|
|
164
|
+
await fs.cp(path.join(repoRoot, "conformance", "v1"), path.join(stagingRoot, "conformance-v1"), { recursive: true });
|
|
165
|
+
const conformanceTgz = path.join(conformanceOutDir, "conformance-v1.tar.gz");
|
|
166
|
+
{
|
|
167
|
+
const tarPath = path.join(conformanceOutDir, "conformance-v1.tar");
|
|
168
|
+
sh("tar", ["--sort=name", "--mtime=2026-02-02 00:00:00Z", "--owner=0", "--group=0", "--numeric-owner", "-cf", tarPath, "-C", stagingRoot, "conformance-v1"]);
|
|
169
|
+
const res = spawnSync("gzip", ["-n", "-9", "-c", tarPath]);
|
|
170
|
+
if (res.status !== 0) throw new Error("gzip failed");
|
|
171
|
+
await fs.writeFile(conformanceTgz, res.stdout);
|
|
172
|
+
await fs.rm(tarPath, { force: true });
|
|
173
|
+
}
|
|
174
|
+
const conformanceSum = await sha256FileHex(conformanceTgz);
|
|
175
|
+
await fs.writeFile(path.join(conformanceOutDir, "conformance-v1-SHA256SUMS"), `${conformanceSum} conformance-v1.tar.gz\n`, "utf8");
|
|
176
|
+
|
|
177
|
+
// 4) Tool provenance summary
|
|
178
|
+
const tool = {
|
|
179
|
+
schemaVersion: "AuditToolSummary.v1",
|
|
180
|
+
tool: { name: "settld-verify", version: toolVersion, commit: toolCommit },
|
|
181
|
+
repo: { commit: toolCommit }
|
|
182
|
+
};
|
|
183
|
+
await fs.writeFile(path.join(packetRoot, "tool.json"), JSON.stringify(tool, null, 2) + "\n", "utf8");
|
|
184
|
+
|
|
185
|
+
// 5) Packet README
|
|
186
|
+
await writeReadme({
|
|
187
|
+
dst: path.join(packetRoot, "README.md"),
|
|
188
|
+
packetName,
|
|
189
|
+
tool
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// 6) SHA256SUMS for all files in packet (excluding SHA256SUMS itself)
|
|
193
|
+
const files = (await listFilesRecursive(packetRoot)).filter((fp) => path.basename(fp) !== "SHA256SUMS");
|
|
194
|
+
const lines = [];
|
|
195
|
+
for (const fp of files) {
|
|
196
|
+
// eslint-disable-next-line no-await-in-loop
|
|
197
|
+
const sum = await sha256FileHex(fp);
|
|
198
|
+
const rel = path.relative(packetRoot, fp).replaceAll(path.sep, "/");
|
|
199
|
+
lines.push(`${sum} ${rel}`);
|
|
200
|
+
}
|
|
201
|
+
await fs.writeFile(path.join(packetRoot, "SHA256SUMS"), lines.join("\n") + "\n", "utf8");
|
|
202
|
+
|
|
203
|
+
// 7) Deterministic zip build (sorted entries, fixed timestamp)
|
|
204
|
+
const zipPath = path.join(outDir, `${packetName}.zip`);
|
|
205
|
+
const py = `
|
|
206
|
+
import os, sys, zipfile
|
|
207
|
+
from datetime import datetime
|
|
208
|
+
|
|
209
|
+
root = sys.argv[1]
|
|
210
|
+
out = sys.argv[2]
|
|
211
|
+
|
|
212
|
+
fixed = (2026, 2, 2, 0, 0, 0)
|
|
213
|
+
|
|
214
|
+
paths = []
|
|
215
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
216
|
+
dirnames.sort()
|
|
217
|
+
filenames.sort()
|
|
218
|
+
for fn in filenames:
|
|
219
|
+
fp = os.path.join(dirpath, fn)
|
|
220
|
+
rel = os.path.relpath(fp, root).replace(os.sep, "/")
|
|
221
|
+
paths.append((fp, rel))
|
|
222
|
+
|
|
223
|
+
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as z:
|
|
224
|
+
for fp, rel in paths:
|
|
225
|
+
zi = zipfile.ZipInfo(rel, date_time=fixed)
|
|
226
|
+
# Normalize perms to 0644 for determinism.
|
|
227
|
+
zi.external_attr = (0o100644 << 16)
|
|
228
|
+
with open(fp, "rb") as f:
|
|
229
|
+
z.writestr(zi, f.read())
|
|
230
|
+
`;
|
|
231
|
+
sh("python3", ["-c", py, packetRoot, zipPath]);
|
|
232
|
+
const zipSum = await sha256FileHex(zipPath);
|
|
233
|
+
await fs.writeFile(path.join(outDir, `${packetName}.zip.sha256`), `${zipSum} ${path.basename(zipPath)}\n`, "utf8");
|
|
234
|
+
|
|
235
|
+
// eslint-disable-next-line no-console
|
|
236
|
+
console.log(`wrote ${zipPath}`);
|
|
237
|
+
} finally {
|
|
238
|
+
await fs.rm(stagingRoot, { recursive: true, force: true });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await main();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ -z "${DATABASE_URL:-}" ]]; then
|
|
5
|
+
echo "DATABASE_URL is required" >&2
|
|
6
|
+
exit 2
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
OUT_DIR="${OUT_DIR:-./backups}"
|
|
10
|
+
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
11
|
+
DEST="${OUT_DIR%/}/backup_${TS}"
|
|
12
|
+
|
|
13
|
+
mkdir -p "$DEST"
|
|
14
|
+
|
|
15
|
+
sanitize_url() {
|
|
16
|
+
# Best-effort: redact password in postgres://user:pass@host/db
|
|
17
|
+
local url="$1"
|
|
18
|
+
if [[ "$url" =~ ^([^:]+://[^:@]+):[^@]+@(.*)$ ]]; then
|
|
19
|
+
echo "${BASH_REMATCH[1]}:[REDACTED]@${BASH_REMATCH[2]}"
|
|
20
|
+
else
|
|
21
|
+
echo "[REDACTED]"
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
echo "Backing up to $DEST" >&2
|
|
26
|
+
|
|
27
|
+
pg_dump --format=custom --no-owner --no-privileges --file "$DEST/db.dump" "$DATABASE_URL"
|
|
28
|
+
|
|
29
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
30
|
+
shasum -a 256 "$DEST/db.dump" >"$DEST/db.dump.sha256"
|
|
31
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
32
|
+
sha256sum "$DEST/db.dump" >"$DEST/db.dump.sha256"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
cat >"$DEST/meta.json" <<EOF
|
|
36
|
+
{
|
|
37
|
+
"createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
38
|
+
"databaseUrl": "$(sanitize_url "$DATABASE_URL")",
|
|
39
|
+
"pgSchema": "${PROXY_PG_SCHEMA:-public}",
|
|
40
|
+
"notes": "pg_dump custom format; restore with scripts/restore-pg.sh"
|
|
41
|
+
}
|
|
42
|
+
EOF
|
|
43
|
+
|
|
44
|
+
echo "Done: $DEST" >&2
|
|
45
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Backup/Restore verification (PG)
|
|
2
|
+
|
|
3
|
+
This folder contains a deterministic backup/restore drill:
|
|
4
|
+
|
|
5
|
+
- Seed a known workload into Postgres (PG store).
|
|
6
|
+
- Capture a small set of state digests (counts + stable hashes).
|
|
7
|
+
- Take a logical backup (`pg_dump`).
|
|
8
|
+
- Restore into a fresh database.
|
|
9
|
+
- Recompute state digests and compare.
|
|
10
|
+
|
|
11
|
+
Entry point: `scripts/backup-restore-test.sh`.
|
|
12
|
+
|
|
13
|
+
Requirements:
|
|
14
|
+
|
|
15
|
+
- `STORE=pg`
|
|
16
|
+
- `DATABASE_URL` and `RESTORE_DATABASE_URL` set
|
|
17
|
+
- `pg_dump` and `psql` available on PATH
|
|
18
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captures a small, stable digest of Postgres state for backup/restore verification.
|
|
3
|
+
*
|
|
4
|
+
* Prints JSON to stdout.
|
|
5
|
+
*/
|
|
6
|
+
import pg from "pg";
|
|
7
|
+
import { canonicalJsonStringify } from "../../src/core/canonical-json.js";
|
|
8
|
+
import { sha256Hex } from "../../src/core/crypto.js";
|
|
9
|
+
import { normalizeTenantId } from "../../src/core/tenancy.js";
|
|
10
|
+
|
|
11
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
12
|
+
if (!DATABASE_URL) throw new Error("DATABASE_URL is required");
|
|
13
|
+
const TENANT_ID = normalizeTenantId(process.env.TENANT_ID ?? "tenant_default");
|
|
14
|
+
const SCHEMA = (() => {
|
|
15
|
+
const raw = String(process.env.PROXY_PG_SCHEMA ?? "public").trim();
|
|
16
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(raw)) {
|
|
17
|
+
throw new Error("PROXY_PG_SCHEMA must match [A-Za-z_][A-Za-z0-9_]*");
|
|
18
|
+
}
|
|
19
|
+
return raw;
|
|
20
|
+
})();
|
|
21
|
+
|
|
22
|
+
function digestRows(rows) {
|
|
23
|
+
return sha256Hex(canonicalJsonStringify(rows));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { Client } = pg;
|
|
27
|
+
const client = new Client({ connectionString: DATABASE_URL });
|
|
28
|
+
await client.connect();
|
|
29
|
+
await client.query(`SET search_path TO "${SCHEMA}", public`);
|
|
30
|
+
|
|
31
|
+
const tenantCountRes = await client.query("SELECT COUNT(DISTINCT tenant_id)::int AS n FROM snapshots");
|
|
32
|
+
const jobCountRes = await client.query("SELECT COUNT(*)::int AS n FROM snapshots WHERE tenant_id = $1 AND aggregate_type = 'job'", [TENANT_ID]);
|
|
33
|
+
const ledgerEntryCountRes = await client.query("SELECT COUNT(*)::int AS n FROM ledger_entries WHERE tenant_id = $1", [TENANT_ID]);
|
|
34
|
+
const allocationCountRes = await client.query("SELECT COUNT(*)::int AS n FROM ledger_allocations WHERE tenant_id = $1", [TENANT_ID]);
|
|
35
|
+
const artifactCountRes = await client.query("SELECT COUNT(*)::int AS n FROM artifacts WHERE tenant_id = $1", [TENANT_ID]);
|
|
36
|
+
const partyStatementCountRes = await client.query("SELECT COUNT(*)::int AS n FROM party_statements WHERE tenant_id = $1", [TENANT_ID]);
|
|
37
|
+
|
|
38
|
+
const artifactsRes = await client.query(
|
|
39
|
+
`
|
|
40
|
+
SELECT artifact_id, artifact_type, artifact_hash
|
|
41
|
+
FROM artifacts
|
|
42
|
+
WHERE tenant_id = $1
|
|
43
|
+
AND artifact_type IN ('SettlementStatement.v1','MonthlyStatement.v1','PartyStatement.v1','PayoutInstruction.v1')
|
|
44
|
+
ORDER BY artifact_type ASC, artifact_id ASC
|
|
45
|
+
`,
|
|
46
|
+
[TENANT_ID]
|
|
47
|
+
);
|
|
48
|
+
const artifactsDigest = digestRows(
|
|
49
|
+
artifactsRes.rows.map((r) => ({
|
|
50
|
+
artifactId: String(r.artifact_id),
|
|
51
|
+
artifactType: String(r.artifact_type),
|
|
52
|
+
artifactHash: String(r.artifact_hash)
|
|
53
|
+
}))
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const ledgerEntriesRes = await client.query(
|
|
57
|
+
`
|
|
58
|
+
SELECT entry_id, entry_json
|
|
59
|
+
FROM ledger_entries
|
|
60
|
+
WHERE tenant_id = $1
|
|
61
|
+
ORDER BY entry_id ASC
|
|
62
|
+
`,
|
|
63
|
+
[TENANT_ID]
|
|
64
|
+
);
|
|
65
|
+
const ledgerDigest = digestRows(
|
|
66
|
+
ledgerEntriesRes.rows.map((r) => ({
|
|
67
|
+
entryId: String(r.entry_id),
|
|
68
|
+
entry: r.entry_json ?? null
|
|
69
|
+
}))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const allocRes = await client.query(
|
|
73
|
+
`
|
|
74
|
+
SELECT entry_id, posting_id, account_id, party_id, party_role, currency, amount_cents
|
|
75
|
+
FROM ledger_allocations
|
|
76
|
+
WHERE tenant_id = $1
|
|
77
|
+
ORDER BY entry_id ASC, posting_id ASC, party_id ASC
|
|
78
|
+
`,
|
|
79
|
+
[TENANT_ID]
|
|
80
|
+
);
|
|
81
|
+
const allocationsDigest = digestRows(
|
|
82
|
+
allocRes.rows.map((r) => ({
|
|
83
|
+
entryId: String(r.entry_id),
|
|
84
|
+
postingId: String(r.posting_id),
|
|
85
|
+
accountId: r.account_id === null ? null : String(r.account_id),
|
|
86
|
+
partyId: String(r.party_id),
|
|
87
|
+
partyRole: String(r.party_role),
|
|
88
|
+
currency: String(r.currency),
|
|
89
|
+
amountCents: Number(r.amount_cents)
|
|
90
|
+
}))
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const monthEventsRes = await client.query(
|
|
94
|
+
`
|
|
95
|
+
SELECT aggregate_id, seq, event_json
|
|
96
|
+
FROM events
|
|
97
|
+
WHERE tenant_id = $1 AND aggregate_type = 'month'
|
|
98
|
+
ORDER BY aggregate_id ASC, seq ASC
|
|
99
|
+
`,
|
|
100
|
+
[TENANT_ID]
|
|
101
|
+
);
|
|
102
|
+
const monthEventsDigest = digestRows(
|
|
103
|
+
monthEventsRes.rows.map((r) => ({
|
|
104
|
+
monthId: String(r.aggregate_id),
|
|
105
|
+
seq: Number(r.seq),
|
|
106
|
+
event: r.event_json ?? null
|
|
107
|
+
}))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
await client.end();
|
|
111
|
+
|
|
112
|
+
const state = {
|
|
113
|
+
tenantId: TENANT_ID,
|
|
114
|
+
counts: {
|
|
115
|
+
tenants: Number(tenantCountRes.rows?.[0]?.n ?? 0),
|
|
116
|
+
jobs: Number(jobCountRes.rows?.[0]?.n ?? 0),
|
|
117
|
+
ledgerEntries: Number(ledgerEntryCountRes.rows?.[0]?.n ?? 0),
|
|
118
|
+
allocations: Number(allocationCountRes.rows?.[0]?.n ?? 0),
|
|
119
|
+
artifacts: Number(artifactCountRes.rows?.[0]?.n ?? 0),
|
|
120
|
+
partyStatements: Number(partyStatementCountRes.rows?.[0]?.n ?? 0)
|
|
121
|
+
},
|
|
122
|
+
digests: {
|
|
123
|
+
artifacts: artifactsDigest,
|
|
124
|
+
ledgerEntries: ledgerDigest,
|
|
125
|
+
allocations: allocationsDigest,
|
|
126
|
+
monthEvents: monthEventsDigest
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
process.stdout.write(JSON.stringify(state, null, 2) + "\n");
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
|
|
3
|
+
import { createApi } from "../../src/api/app.js";
|
|
4
|
+
import { createPgStore } from "../../src/db/store-pg.js";
|
|
5
|
+
import { authKeyId, authKeySecret, hashAuthKeySecretLegacy } from "../../src/core/auth.js";
|
|
6
|
+
import { DEFAULT_TENANT_ID, normalizeTenantId } from "../../src/core/tenancy.js";
|
|
7
|
+
|
|
8
|
+
function makeReq({ method, path, headers, body }) {
|
|
9
|
+
const chunks = body === undefined ? [] : [Buffer.from(JSON.stringify(body), "utf8")];
|
|
10
|
+
const req = Readable.from(chunks);
|
|
11
|
+
req.method = method;
|
|
12
|
+
req.url = path;
|
|
13
|
+
req.headers = headers ?? {};
|
|
14
|
+
return req;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeRes() {
|
|
18
|
+
const headers = new Map();
|
|
19
|
+
return {
|
|
20
|
+
statusCode: 200,
|
|
21
|
+
setHeader(name, value) {
|
|
22
|
+
headers.set(String(name).toLowerCase(), String(value));
|
|
23
|
+
},
|
|
24
|
+
end(payload) {
|
|
25
|
+
this.body = payload ?? "";
|
|
26
|
+
this.headers = headers;
|
|
27
|
+
this.ended = true;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasAuthHeader(headers) {
|
|
33
|
+
const keys = Object.keys(headers ?? {});
|
|
34
|
+
for (const k of keys) {
|
|
35
|
+
const key = String(k).toLowerCase();
|
|
36
|
+
if (key === "authorization" || key === "x-proxy-api-key" || key === "x-proxy-ops-token") return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function ensureAuth({ store, tenantId, scopes }) {
|
|
42
|
+
tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
43
|
+
const keyId = authKeyId();
|
|
44
|
+
const secret = authKeySecret();
|
|
45
|
+
const secretHash = hashAuthKeySecretLegacy(secret);
|
|
46
|
+
const createdAt = new Date().toISOString();
|
|
47
|
+
await store.putAuthKey({
|
|
48
|
+
tenantId,
|
|
49
|
+
authKey: { keyId, secretHash, scopes, status: "active", description: "backup-restore-drill", createdAt }
|
|
50
|
+
});
|
|
51
|
+
const token = `${keyId}.${secret}`;
|
|
52
|
+
return { token, authorization: `Bearer ${token}` };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function createBackupRestoreApiClient({
|
|
56
|
+
databaseUrl,
|
|
57
|
+
schema = null,
|
|
58
|
+
tenantId = DEFAULT_TENANT_ID,
|
|
59
|
+
scopes = ["ops_write", "finance_write", "audit_read"],
|
|
60
|
+
protocol = "1.0",
|
|
61
|
+
now = null
|
|
62
|
+
} = {}) {
|
|
63
|
+
if (!databaseUrl) throw new Error("databaseUrl is required");
|
|
64
|
+
tenantId = normalizeTenantId(tenantId ?? DEFAULT_TENANT_ID);
|
|
65
|
+
const store = await createPgStore({
|
|
66
|
+
databaseUrl,
|
|
67
|
+
schema: schema ?? (process.env.PROXY_PG_SCHEMA ?? "public"),
|
|
68
|
+
migrateOnStartup: true
|
|
69
|
+
});
|
|
70
|
+
const api = createApi({ store, ...(typeof now === "function" ? { now } : null) });
|
|
71
|
+
|
|
72
|
+
const auth = await ensureAuth({ store, tenantId, scopes });
|
|
73
|
+
|
|
74
|
+
async function request({ method, path, headers = {}, body } = {}) {
|
|
75
|
+
const reqHeaders = {
|
|
76
|
+
"x-proxy-tenant-id": tenantId,
|
|
77
|
+
"x-settld-protocol": protocol,
|
|
78
|
+
...(headers ?? {})
|
|
79
|
+
};
|
|
80
|
+
if (body !== undefined) reqHeaders["content-type"] = "application/json";
|
|
81
|
+
if (!hasAuthHeader(reqHeaders)) reqHeaders.authorization = auth.authorization;
|
|
82
|
+
const req = makeReq({ method, path, headers: reqHeaders, body });
|
|
83
|
+
const res = makeRes();
|
|
84
|
+
await api.handle(req, res);
|
|
85
|
+
const text = typeof res.body === "string" ? res.body : Buffer.from(res.body ?? "").toString("utf8");
|
|
86
|
+
const contentType = res.headers?.get?.("content-type") ? String(res.headers.get("content-type")) : "";
|
|
87
|
+
const isJson = contentType.includes("application/json") || contentType.includes("+json");
|
|
88
|
+
const json = isJson && text ? JSON.parse(text) : null;
|
|
89
|
+
return { statusCode: res.statusCode, json, body: text, headers: res.headers };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function close() {
|
|
93
|
+
await store.close?.();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { store, api, request, close, tenantId };
|
|
97
|
+
}
|