xytara 3.1.13 → 3.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.env.example +38 -4
  2. package/BSV_TERANODE_PLATFORM_DISCONTINUITY_RUNBOOK.md +126 -126
  3. package/REAL_PAYMENT_SETUP.md +14 -7
  4. package/RELEASE_NOTES.md +9 -4
  5. package/fixtures/catalog.js +22 -0
  6. package/lib/a2a_rpc.js +287 -280
  7. package/lib/account_auth.js +585 -585
  8. package/lib/agent_identity.js +162 -162
  9. package/lib/asyncapi_contract.js +3 -3
  10. package/lib/bolt_adapter.js +187 -187
  11. package/lib/bolt_payment_front.js +224 -224
  12. package/lib/buyer_event_log.js +215 -0
  13. package/lib/buyer_safety_policy.js +132 -0
  14. package/lib/checkout_processor_adapter.js +217 -217
  15. package/lib/command_flow.js +911 -722
  16. package/lib/commerce_artifacts.js +591 -591
  17. package/lib/commerce_bridge.js +274 -274
  18. package/lib/commerce_cases.js +399 -399
  19. package/lib/commerce_client.js +7694 -7694
  20. package/lib/commerce_quotes.js +29 -0
  21. package/lib/commerce_runtime.js +322 -322
  22. package/lib/execution_dedupe.js +86 -86
  23. package/lib/external_credit_bridge.js +142 -142
  24. package/lib/http_transport.js +99 -81
  25. package/lib/l402_adapter.js +241 -241
  26. package/lib/l402_payment_front.js +265 -265
  27. package/lib/mpp_adapter.js +237 -237
  28. package/lib/openapi_contract.js +4 -0
  29. package/lib/payment_config.js +257 -253
  30. package/lib/payment_verification.js +357 -357
  31. package/lib/provider_payout_execution.js +155 -155
  32. package/lib/provider_registry.js +852 -842
  33. package/lib/rate_limiter.js +81 -81
  34. package/lib/runtime_state_store.js +495 -369
  35. package/lib/settlement_bsv_live.js +600 -600
  36. package/lib/settlement_bsv_teranode.js +117 -117
  37. package/lib/stripe_mpp_payment_front.js +258 -258
  38. package/lib/x402_facilitator.js +198 -191
  39. package/lib/x402_lane_contract.js +5 -3
  40. package/lib/x402_settlement_recovery.js +175 -0
  41. package/package.json +9 -3
  42. package/scripts/buyer_events_daily_report.js +79 -0
  43. package/scripts/state_backup_offsite.js +196 -0
  44. package/scripts/verify_a2a_rpc.js +131 -105
  45. package/scripts/verify_agent_identity.js +153 -153
  46. package/scripts/verify_all.js +1 -1
  47. package/scripts/verify_bolt_adapter.js +128 -128
  48. package/scripts/verify_buyer_event_log.js +123 -0
  49. package/scripts/verify_buyer_safety.js +141 -0
  50. package/scripts/verify_cdp_facilitator.js +110 -110
  51. package/scripts/verify_checkout_processor.js +109 -109
  52. package/scripts/verify_commerce_bridge.js +177 -177
  53. package/scripts/verify_execution_dedupe.js +189 -189
  54. package/scripts/verify_execution_surfacing.js +76 -76
  55. package/scripts/verify_final_activation.js +191 -191
  56. package/scripts/verify_l402_adapter.js +86 -86
  57. package/scripts/verify_lifecycle_spend_binding.js +88 -0
  58. package/scripts/verify_mpp_adapter.js +128 -128
  59. package/scripts/verify_production_readiness.js +268 -268
  60. package/scripts/verify_rate_limiter.js +52 -52
  61. package/scripts/verify_refund_credit.js +106 -106
  62. package/scripts/verify_release_candidate.js +81 -81
  63. package/scripts/verify_runtime_state_guard.js +117 -0
  64. package/scripts/verify_service.js +15421 -15421
  65. package/scripts/verify_verify_execute_settle.js +209 -208
  66. package/scripts/verify_x402_facilitator.js +307 -286
  67. package/scripts/verify_x402_settlement_recovery.js +153 -0
  68. package/server.js +12242 -11974
@@ -1,268 +1,268 @@
1
- "use strict";
2
-
3
- const assert = require("assert");
4
- const fs = require("fs");
5
- const path = require("path");
6
- const { execFileSync } = require("child_process");
7
-
8
- function firstEnv(...names) {
9
- for (const name of names) {
10
- const value = process.env[name];
11
- if (typeof value === "string" && value.trim().length > 0) {
12
- return value.trim();
13
- }
14
- }
15
- return "";
16
- }
17
-
18
- function ensureReleaseCandidate() {
19
- const releaseOutput = execFileSync(process.execPath, [
20
- path.resolve(__dirname, "..", "bin", "xytara-release.js"),
21
- "--candidate",
22
- "--summary"
23
- ], {
24
- cwd: path.resolve(__dirname, ".."),
25
- encoding: "utf8"
26
- });
27
- const releaseCandidate = JSON.parse(releaseOutput);
28
- assert.strictEqual(releaseCandidate.product, "xytara", "release candidate product mismatch");
29
- }
30
-
31
- function ensurePersistenceConfig() {
32
- const stateFile = process.env.XYTARA_STATE_FILE;
33
- const supabaseUrl = process.env.XYTARA_SUPABASE_URL;
34
- const supabaseServiceRoleKey = process.env.XYTARA_SUPABASE_SERVICE_ROLE_KEY;
35
-
36
- const fileConfigured = typeof stateFile === "string" && stateFile.trim().length > 0;
37
- const supabaseConfigured =
38
- typeof supabaseUrl === "string" &&
39
- supabaseUrl.trim().length > 0 &&
40
- typeof supabaseServiceRoleKey === "string" &&
41
- supabaseServiceRoleKey.trim().length > 0;
42
-
43
- assert.strictEqual(
44
- fileConfigured || supabaseConfigured,
45
- true,
46
- "configure XYTARA_STATE_FILE on persistent disk or XYTARA_SUPABASE_URL + XYTARA_SUPABASE_SERVICE_ROLE_KEY before treating xytara as production-ready"
47
- );
48
-
49
- if (fileConfigured) {
50
- const resolvedPath = path.resolve(stateFile);
51
- const parentDirectory = path.dirname(resolvedPath);
52
- assert.strictEqual(
53
- fs.existsSync(parentDirectory),
54
- true,
55
- `XYTARA_STATE_FILE parent directory does not exist: ${parentDirectory}`
56
- );
57
- fs.accessSync(parentDirectory, fs.constants.W_OK);
58
- }
59
- }
60
-
61
- function ensureProductionSecurityConfig() {
62
- const verificationMode = String(process.env.XYTARA_PAYMENT_VERIFICATION_MODE || "").trim() || "presence";
63
- assert.notStrictEqual(
64
- verificationMode,
65
- "presence",
66
- "set XYTARA_PAYMENT_VERIFICATION_MODE to local_signed or stronger before treating xytara as production-ready"
67
- );
68
-
69
- if (verificationMode === "local_signed") {
70
- const walletRegistryJson = String(process.env.XYTARA_WALLET_REGISTRY_JSON || "").trim();
71
- assert.strictEqual(walletRegistryJson.length > 0, true, "configure XYTARA_WALLET_REGISTRY_JSON before using local_signed payment verification");
72
- const parsed = JSON.parse(walletRegistryJson);
73
- assert.strictEqual(Array.isArray(parsed), true, "XYTARA_WALLET_REGISTRY_JSON must be a JSON array");
74
- assert.strictEqual(parsed.length > 0, true, "register at least one enabled wallet in XYTARA_WALLET_REGISTRY_JSON before treating xytara as production-ready");
75
- }
76
-
77
- assert.strictEqual(
78
- String(process.env.XYTARA_CREDIT_BRIDGE_BEARER_TOKEN || "").trim().length > 0,
79
- true,
80
- "configure XYTARA_CREDIT_BRIDGE_BEARER_TOKEN before treating xytara as production-ready"
81
- );
82
- assert.strictEqual(
83
- String(process.env.XYTARA_ACCOUNT_AUTH_BEARER_TOKEN || "").trim().length > 0,
84
- true,
85
- "configure XYTARA_ACCOUNT_AUTH_BEARER_TOKEN before treating xytara as production-ready"
86
- );
87
- }
88
-
89
- function ensureTreasuryConfig() {
90
- const treasuryPath = String(process.env.XYTARA_TREASURY_DESTINATIONS_PATH || "").trim();
91
- assert.strictEqual(
92
- treasuryPath.length > 0,
93
- true,
94
- "configure XYTARA_TREASURY_DESTINATIONS_PATH before treating xytara as production-ready"
95
- );
96
- const resolvedPath = path.resolve(treasuryPath);
97
- assert.strictEqual(fs.existsSync(resolvedPath), true, `treasury destinations file does not exist: ${resolvedPath}`);
98
- const parsed = JSON.parse(fs.readFileSync(resolvedPath, "utf8"));
99
- const configuredRails = Object.values(parsed || {}).filter((entry) =>
100
- entry
101
- && typeof entry === "object"
102
- && typeof entry.external_ref === "string"
103
- && entry.external_ref.trim().length > 0
104
- );
105
- assert.strictEqual(
106
- configuredRails.length > 0,
107
- true,
108
- "configure at least one treasury destination external_ref before treating xytara as production-ready"
109
- );
110
- }
111
-
112
- function ensureCallbackDeliveryConfig() {
113
- const callbackDeliveryEnabled = String(process.env.XYTARA_CALLBACK_DELIVERY_ENABLED || "true").trim().toLowerCase() !== "false";
114
- if (!callbackDeliveryEnabled) return;
115
- assert.strictEqual(
116
- String(process.env.XYTARA_CALLBACK_SIGNING_SECRET || "").trim().length > 0,
117
- true,
118
- "configure XYTARA_CALLBACK_SIGNING_SECRET before treating xytara as production-ready"
119
- );
120
- }
121
-
122
- function ensureHostedCheckoutIngressSecurity() {
123
- const verificationRequired = String(
124
- process.env.XYTARA_CHECKOUT_WEBHOOK_VERIFICATION_REQUIRED
125
- || process.env.XYTARA_HOSTED_CHECKOUT_ENABLED
126
- || "true"
127
- ).trim().toLowerCase();
128
- if (["false", "0", "no", "off"].includes(verificationRequired)) {
129
- return;
130
- }
131
- assert.strictEqual(
132
- String(process.env.XYTARA_CHECKOUT_WEBHOOK_SECRET || process.env.XYTARA_HOSTED_CHECKOUT_WEBHOOK_SECRET || "").trim().length > 0,
133
- true,
134
- "configure XYTARA_CHECKOUT_WEBHOOK_SECRET before treating hosted-checkout ingress as production-ready"
135
- );
136
- }
137
-
138
- function ensureL402Config() {
139
- const l402Enabled = String(process.env.XYTARA_L402_ENABLED || "false").trim().toLowerCase() === "true";
140
- if (!l402Enabled) return;
141
- assert.strictEqual(
142
- String(process.env.XYTARA_L402_SHARED_SECRET || "").trim().length > 0,
143
- true,
144
- "configure XYTARA_L402_SHARED_SECRET before treating the L402 ingress as production-ready"
145
- );
146
- }
147
-
148
- function ensureBoltConfig() {
149
- const boltEnabled = String(process.env.XYTARA_BOLT_ENABLED || "false").trim().toLowerCase() === "true";
150
- if (!boltEnabled) return;
151
- assert.strictEqual(
152
- String(process.env.XYTARA_BOLT_SHARED_SECRET || "").trim().length > 0,
153
- true,
154
- "configure XYTARA_BOLT_SHARED_SECRET before treating the BOLT ingress as production-ready"
155
- );
156
- }
157
-
158
- function ensureMppConfig() {
159
- const mppEnabled = String(process.env.XYTARA_MPP_ENABLED || "false").trim().toLowerCase() === "true";
160
- if (!mppEnabled) return;
161
- assert.strictEqual(
162
- String(process.env.XYTARA_MPP_SHARED_SECRET || "").trim().length > 0,
163
- true,
164
- "configure XYTARA_MPP_SHARED_SECRET before treating the MPP ingress as production-ready"
165
- );
166
- }
167
-
168
- function ensureRateLimitConfig() {
169
- const rateLimitEnabled = String(process.env.XYTARA_RATE_LIMIT_ENABLED || "true").trim().toLowerCase() !== "false";
170
- assert.strictEqual(
171
- rateLimitEnabled,
172
- true,
173
- "keep XYTARA_RATE_LIMIT_ENABLED=true before treating xytara as production-ready unless you have an equivalent upstream control"
174
- );
175
- const windowMs = Number(process.env.XYTARA_RATE_LIMIT_WINDOW_MS || 0);
176
- const maxRequests = Number(process.env.XYTARA_RATE_LIMIT_MAX_REQUESTS || 0);
177
- assert.strictEqual(Number.isFinite(windowMs) && windowMs > 0, true, "configure XYTARA_RATE_LIMIT_WINDOW_MS to a positive value");
178
- assert.strictEqual(Number.isFinite(maxRequests) && maxRequests > 0, true, "configure XYTARA_RATE_LIMIT_MAX_REQUESTS to a positive value");
179
- }
180
-
181
- function ensureExecutionTargetConfig() {
182
- const raw = String(process.env.XYTARA_EXECUTION_TARGETS_JSON || "").trim();
183
- const requireTargets = String(process.env.XYTARA_EXTERNAL_EXECUTION_REQUIRED || "false").trim().toLowerCase() === "true";
184
- if (!raw) {
185
- assert.strictEqual(
186
- requireTargets,
187
- false,
188
- "configure XYTARA_EXECUTION_TARGETS_JSON before treating xytara as production-ready when XYTARA_EXTERNAL_EXECUTION_REQUIRED=true"
189
- );
190
- return;
191
- }
192
-
193
- const parsed = JSON.parse(raw);
194
- assert.strictEqual(Array.isArray(parsed), true, "XYTARA_EXECUTION_TARGETS_JSON must be a JSON array");
195
- assert.strictEqual(parsed.length > 0, true, "configure at least one execution target in XYTARA_EXECUTION_TARGETS_JSON");
196
- parsed.forEach((entry, index) => {
197
- assert.strictEqual(
198
- entry && typeof entry === "object" && !Array.isArray(entry),
199
- true,
200
- `execution target ${index + 1} must be an object`
201
- );
202
- assert.strictEqual(
203
- typeof entry.endpoint_url === "string" && entry.endpoint_url.trim().length > 0,
204
- true,
205
- `execution target ${index + 1} must include endpoint_url`
206
- );
207
- assert.strictEqual(
208
- Boolean(
209
- (typeof entry.task_ref === "string" && entry.task_ref.trim().length > 0)
210
- || (typeof entry.tool_name === "string" && entry.tool_name.trim().length > 0)
211
- || (typeof entry.integration_id === "string" && entry.integration_id.trim().length > 0)
212
- ),
213
- true,
214
- `execution target ${index + 1} must match at least one of task_ref, tool_name, or integration_id`
215
- );
216
- });
217
- }
218
-
219
- function ensureNativeSettlementConfig() {
220
- const runtimeMode = String(
221
- firstEnv("XYTARA_BSV_SETTLEMENT_RUNTIME_MODE", "XYTARA_BSV_TERANODE_SETTLEMENT_RUNTIME_MODE") || "mock"
222
- ).trim().toLowerCase() || "mock";
223
- assert.notStrictEqual(
224
- runtimeMode,
225
- "mock",
226
- "configure XYTARA_BSV_SETTLEMENT_RUNTIME_MODE or XYTARA_BSV_TERANODE_SETTLEMENT_RUNTIME_MODE to observe, arc, or another live runtime mode before treating xytara as production-ready"
227
- );
228
- assert.strictEqual(
229
- firstEnv(
230
- "XYTARA_BSV_MERCHANT_ADDRESS",
231
- "XYTARA_BSV_TERANODE_MERCHANT_ADDRESS",
232
- "XYTARA_BSV_MERCHANT_PAYMAIL",
233
- "XYTARA_BSV_TERANODE_MERCHANT_PAYMAIL"
234
- ).length > 0,
235
- true,
236
- "configure XYTARA_BSV_MERCHANT_ADDRESS / XYTARA_BSV_MERCHANT_PAYMAIL or the legacy XYTARA_BSV_TERANODE_* merchant vars before treating xytara as production-ready"
237
- );
238
- if (runtimeMode === "arc") {
239
- assert.strictEqual(
240
- firstEnv("XYTARA_BSV_ARC_RPC_URL", "XYTARA_BSV_ARC_URL", "XYTARA_BSV_TERANODE_RPC_URL").length > 0,
241
- true,
242
- "configure XYTARA_BSV_ARC_RPC_URL, XYTARA_BSV_ARC_URL, or XYTARA_BSV_TERANODE_RPC_URL before treating arc settlement mode as production-ready"
243
- );
244
- assert.strictEqual(
245
- firstEnv("XYTARA_BSV_ARC_RPC_API_KEY", "XYTARA_BSV_ARC_API_KEY", "XYTARA_BSV_TERANODE_RPC_API_KEY").length > 0,
246
- true,
247
- "configure XYTARA_BSV_ARC_RPC_API_KEY, XYTARA_BSV_ARC_API_KEY, or XYTARA_BSV_TERANODE_RPC_API_KEY before treating arc settlement mode as production-ready"
248
- );
249
- }
250
- }
251
-
252
- function main() {
253
- ensureReleaseCandidate();
254
- ensurePersistenceConfig();
255
- ensureProductionSecurityConfig();
256
- ensureTreasuryConfig();
257
- ensureCallbackDeliveryConfig();
258
- ensureHostedCheckoutIngressSecurity();
259
- ensureL402Config();
260
- ensureBoltConfig();
261
- ensureMppConfig();
262
- ensureRateLimitConfig();
263
- ensureExecutionTargetConfig();
264
- ensureNativeSettlementConfig();
265
- console.log("xytara verify:production-readiness passed");
266
- }
267
-
268
- main();
1
+ "use strict";
2
+
3
+ const assert = require("assert");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { execFileSync } = require("child_process");
7
+
8
+ function firstEnv(...names) {
9
+ for (const name of names) {
10
+ const value = process.env[name];
11
+ if (typeof value === "string" && value.trim().length > 0) {
12
+ return value.trim();
13
+ }
14
+ }
15
+ return "";
16
+ }
17
+
18
+ function ensureReleaseCandidate() {
19
+ const releaseOutput = execFileSync(process.execPath, [
20
+ path.resolve(__dirname, "..", "bin", "xytara-release.js"),
21
+ "--candidate",
22
+ "--summary"
23
+ ], {
24
+ cwd: path.resolve(__dirname, ".."),
25
+ encoding: "utf8"
26
+ });
27
+ const releaseCandidate = JSON.parse(releaseOutput);
28
+ assert.strictEqual(releaseCandidate.product, "xytara", "release candidate product mismatch");
29
+ }
30
+
31
+ function ensurePersistenceConfig() {
32
+ const stateFile = process.env.XYTARA_STATE_FILE;
33
+ const supabaseUrl = process.env.XYTARA_SUPABASE_URL;
34
+ const supabaseServiceRoleKey = process.env.XYTARA_SUPABASE_SERVICE_ROLE_KEY;
35
+
36
+ const fileConfigured = typeof stateFile === "string" && stateFile.trim().length > 0;
37
+ const supabaseConfigured =
38
+ typeof supabaseUrl === "string" &&
39
+ supabaseUrl.trim().length > 0 &&
40
+ typeof supabaseServiceRoleKey === "string" &&
41
+ supabaseServiceRoleKey.trim().length > 0;
42
+
43
+ assert.strictEqual(
44
+ fileConfigured || supabaseConfigured,
45
+ true,
46
+ "configure XYTARA_STATE_FILE on persistent disk or XYTARA_SUPABASE_URL + XYTARA_SUPABASE_SERVICE_ROLE_KEY before treating xytara as production-ready"
47
+ );
48
+
49
+ if (fileConfigured) {
50
+ const resolvedPath = path.resolve(stateFile);
51
+ const parentDirectory = path.dirname(resolvedPath);
52
+ assert.strictEqual(
53
+ fs.existsSync(parentDirectory),
54
+ true,
55
+ `XYTARA_STATE_FILE parent directory does not exist: ${parentDirectory}`
56
+ );
57
+ fs.accessSync(parentDirectory, fs.constants.W_OK);
58
+ }
59
+ }
60
+
61
+ function ensureProductionSecurityConfig() {
62
+ const verificationMode = String(process.env.XYTARA_PAYMENT_VERIFICATION_MODE || "").trim() || "presence";
63
+ assert.notStrictEqual(
64
+ verificationMode,
65
+ "presence",
66
+ "set XYTARA_PAYMENT_VERIFICATION_MODE to local_signed or stronger before treating xytara as production-ready"
67
+ );
68
+
69
+ if (verificationMode === "local_signed") {
70
+ const walletRegistryJson = String(process.env.XYTARA_WALLET_REGISTRY_JSON || "").trim();
71
+ assert.strictEqual(walletRegistryJson.length > 0, true, "configure XYTARA_WALLET_REGISTRY_JSON before using local_signed payment verification");
72
+ const parsed = JSON.parse(walletRegistryJson);
73
+ assert.strictEqual(Array.isArray(parsed), true, "XYTARA_WALLET_REGISTRY_JSON must be a JSON array");
74
+ assert.strictEqual(parsed.length > 0, true, "register at least one enabled wallet in XYTARA_WALLET_REGISTRY_JSON before treating xytara as production-ready");
75
+ }
76
+
77
+ assert.strictEqual(
78
+ String(process.env.XYTARA_CREDIT_BRIDGE_BEARER_TOKEN || "").trim().length > 0,
79
+ true,
80
+ "configure XYTARA_CREDIT_BRIDGE_BEARER_TOKEN before treating xytara as production-ready"
81
+ );
82
+ assert.strictEqual(
83
+ String(process.env.XYTARA_ACCOUNT_AUTH_BEARER_TOKEN || "").trim().length > 0,
84
+ true,
85
+ "configure XYTARA_ACCOUNT_AUTH_BEARER_TOKEN before treating xytara as production-ready"
86
+ );
87
+ }
88
+
89
+ function ensureTreasuryConfig() {
90
+ const treasuryPath = String(process.env.XYTARA_TREASURY_DESTINATIONS_PATH || "").trim();
91
+ assert.strictEqual(
92
+ treasuryPath.length > 0,
93
+ true,
94
+ "configure XYTARA_TREASURY_DESTINATIONS_PATH before treating xytara as production-ready"
95
+ );
96
+ const resolvedPath = path.resolve(treasuryPath);
97
+ assert.strictEqual(fs.existsSync(resolvedPath), true, `treasury destinations file does not exist: ${resolvedPath}`);
98
+ const parsed = JSON.parse(fs.readFileSync(resolvedPath, "utf8"));
99
+ const configuredRails = Object.values(parsed || {}).filter((entry) =>
100
+ entry
101
+ && typeof entry === "object"
102
+ && typeof entry.external_ref === "string"
103
+ && entry.external_ref.trim().length > 0
104
+ );
105
+ assert.strictEqual(
106
+ configuredRails.length > 0,
107
+ true,
108
+ "configure at least one treasury destination external_ref before treating xytara as production-ready"
109
+ );
110
+ }
111
+
112
+ function ensureCallbackDeliveryConfig() {
113
+ const callbackDeliveryEnabled = String(process.env.XYTARA_CALLBACK_DELIVERY_ENABLED || "true").trim().toLowerCase() !== "false";
114
+ if (!callbackDeliveryEnabled) return;
115
+ assert.strictEqual(
116
+ String(process.env.XYTARA_CALLBACK_SIGNING_SECRET || "").trim().length > 0,
117
+ true,
118
+ "configure XYTARA_CALLBACK_SIGNING_SECRET before treating xytara as production-ready"
119
+ );
120
+ }
121
+
122
+ function ensureHostedCheckoutIngressSecurity() {
123
+ const verificationRequired = String(
124
+ process.env.XYTARA_CHECKOUT_WEBHOOK_VERIFICATION_REQUIRED
125
+ || process.env.XYTARA_HOSTED_CHECKOUT_ENABLED
126
+ || "true"
127
+ ).trim().toLowerCase();
128
+ if (["false", "0", "no", "off"].includes(verificationRequired)) {
129
+ return;
130
+ }
131
+ assert.strictEqual(
132
+ String(process.env.XYTARA_CHECKOUT_WEBHOOK_SECRET || process.env.XYTARA_HOSTED_CHECKOUT_WEBHOOK_SECRET || "").trim().length > 0,
133
+ true,
134
+ "configure XYTARA_CHECKOUT_WEBHOOK_SECRET before treating hosted-checkout ingress as production-ready"
135
+ );
136
+ }
137
+
138
+ function ensureL402Config() {
139
+ const l402Enabled = String(process.env.XYTARA_L402_ENABLED || "false").trim().toLowerCase() === "true";
140
+ if (!l402Enabled) return;
141
+ assert.strictEqual(
142
+ String(process.env.XYTARA_L402_SHARED_SECRET || "").trim().length > 0,
143
+ true,
144
+ "configure XYTARA_L402_SHARED_SECRET before treating the L402 ingress as production-ready"
145
+ );
146
+ }
147
+
148
+ function ensureBoltConfig() {
149
+ const boltEnabled = String(process.env.XYTARA_BOLT_ENABLED || "false").trim().toLowerCase() === "true";
150
+ if (!boltEnabled) return;
151
+ assert.strictEqual(
152
+ String(process.env.XYTARA_BOLT_SHARED_SECRET || "").trim().length > 0,
153
+ true,
154
+ "configure XYTARA_BOLT_SHARED_SECRET before treating the BOLT ingress as production-ready"
155
+ );
156
+ }
157
+
158
+ function ensureMppConfig() {
159
+ const mppEnabled = String(process.env.XYTARA_MPP_ENABLED || "false").trim().toLowerCase() === "true";
160
+ if (!mppEnabled) return;
161
+ assert.strictEqual(
162
+ String(process.env.XYTARA_MPP_SHARED_SECRET || "").trim().length > 0,
163
+ true,
164
+ "configure XYTARA_MPP_SHARED_SECRET before treating the MPP ingress as production-ready"
165
+ );
166
+ }
167
+
168
+ function ensureRateLimitConfig() {
169
+ const rateLimitEnabled = String(process.env.XYTARA_RATE_LIMIT_ENABLED || "true").trim().toLowerCase() !== "false";
170
+ assert.strictEqual(
171
+ rateLimitEnabled,
172
+ true,
173
+ "keep XYTARA_RATE_LIMIT_ENABLED=true before treating xytara as production-ready unless you have an equivalent upstream control"
174
+ );
175
+ const windowMs = Number(process.env.XYTARA_RATE_LIMIT_WINDOW_MS || 0);
176
+ const maxRequests = Number(process.env.XYTARA_RATE_LIMIT_MAX_REQUESTS || 0);
177
+ assert.strictEqual(Number.isFinite(windowMs) && windowMs > 0, true, "configure XYTARA_RATE_LIMIT_WINDOW_MS to a positive value");
178
+ assert.strictEqual(Number.isFinite(maxRequests) && maxRequests > 0, true, "configure XYTARA_RATE_LIMIT_MAX_REQUESTS to a positive value");
179
+ }
180
+
181
+ function ensureExecutionTargetConfig() {
182
+ const raw = String(process.env.XYTARA_EXECUTION_TARGETS_JSON || "").trim();
183
+ const requireTargets = String(process.env.XYTARA_EXTERNAL_EXECUTION_REQUIRED || "false").trim().toLowerCase() === "true";
184
+ if (!raw) {
185
+ assert.strictEqual(
186
+ requireTargets,
187
+ false,
188
+ "configure XYTARA_EXECUTION_TARGETS_JSON before treating xytara as production-ready when XYTARA_EXTERNAL_EXECUTION_REQUIRED=true"
189
+ );
190
+ return;
191
+ }
192
+
193
+ const parsed = JSON.parse(raw);
194
+ assert.strictEqual(Array.isArray(parsed), true, "XYTARA_EXECUTION_TARGETS_JSON must be a JSON array");
195
+ assert.strictEqual(parsed.length > 0, true, "configure at least one execution target in XYTARA_EXECUTION_TARGETS_JSON");
196
+ parsed.forEach((entry, index) => {
197
+ assert.strictEqual(
198
+ entry && typeof entry === "object" && !Array.isArray(entry),
199
+ true,
200
+ `execution target ${index + 1} must be an object`
201
+ );
202
+ assert.strictEqual(
203
+ typeof entry.endpoint_url === "string" && entry.endpoint_url.trim().length > 0,
204
+ true,
205
+ `execution target ${index + 1} must include endpoint_url`
206
+ );
207
+ assert.strictEqual(
208
+ Boolean(
209
+ (typeof entry.task_ref === "string" && entry.task_ref.trim().length > 0)
210
+ || (typeof entry.tool_name === "string" && entry.tool_name.trim().length > 0)
211
+ || (typeof entry.integration_id === "string" && entry.integration_id.trim().length > 0)
212
+ ),
213
+ true,
214
+ `execution target ${index + 1} must match at least one of task_ref, tool_name, or integration_id`
215
+ );
216
+ });
217
+ }
218
+
219
+ function ensureNativeSettlementConfig() {
220
+ const runtimeMode = String(
221
+ firstEnv("XYTARA_BSV_SETTLEMENT_RUNTIME_MODE", "XYTARA_BSV_TERANODE_SETTLEMENT_RUNTIME_MODE") || "mock"
222
+ ).trim().toLowerCase() || "mock";
223
+ assert.notStrictEqual(
224
+ runtimeMode,
225
+ "mock",
226
+ "configure XYTARA_BSV_SETTLEMENT_RUNTIME_MODE or XYTARA_BSV_TERANODE_SETTLEMENT_RUNTIME_MODE to observe, arc, or another live runtime mode before treating xytara as production-ready"
227
+ );
228
+ assert.strictEqual(
229
+ firstEnv(
230
+ "XYTARA_BSV_MERCHANT_ADDRESS",
231
+ "XYTARA_BSV_TERANODE_MERCHANT_ADDRESS",
232
+ "XYTARA_BSV_MERCHANT_PAYMAIL",
233
+ "XYTARA_BSV_TERANODE_MERCHANT_PAYMAIL"
234
+ ).length > 0,
235
+ true,
236
+ "configure XYTARA_BSV_MERCHANT_ADDRESS / XYTARA_BSV_MERCHANT_PAYMAIL or the legacy XYTARA_BSV_TERANODE_* merchant vars before treating xytara as production-ready"
237
+ );
238
+ if (runtimeMode === "arc") {
239
+ assert.strictEqual(
240
+ firstEnv("XYTARA_BSV_ARC_RPC_URL", "XYTARA_BSV_ARC_URL", "XYTARA_BSV_TERANODE_RPC_URL").length > 0,
241
+ true,
242
+ "configure XYTARA_BSV_ARC_RPC_URL, XYTARA_BSV_ARC_URL, or XYTARA_BSV_TERANODE_RPC_URL before treating arc settlement mode as production-ready"
243
+ );
244
+ assert.strictEqual(
245
+ firstEnv("XYTARA_BSV_ARC_RPC_API_KEY", "XYTARA_BSV_ARC_API_KEY", "XYTARA_BSV_TERANODE_RPC_API_KEY").length > 0,
246
+ true,
247
+ "configure XYTARA_BSV_ARC_RPC_API_KEY, XYTARA_BSV_ARC_API_KEY, or XYTARA_BSV_TERANODE_RPC_API_KEY before treating arc settlement mode as production-ready"
248
+ );
249
+ }
250
+ }
251
+
252
+ function main() {
253
+ ensureReleaseCandidate();
254
+ ensurePersistenceConfig();
255
+ ensureProductionSecurityConfig();
256
+ ensureTreasuryConfig();
257
+ ensureCallbackDeliveryConfig();
258
+ ensureHostedCheckoutIngressSecurity();
259
+ ensureL402Config();
260
+ ensureBoltConfig();
261
+ ensureMppConfig();
262
+ ensureRateLimitConfig();
263
+ ensureExecutionTargetConfig();
264
+ ensureNativeSettlementConfig();
265
+ console.log("xytara verify:production-readiness passed");
266
+ }
267
+
268
+ main();
@@ -1,52 +1,52 @@
1
- "use strict";
2
-
3
- // Verifies the in-memory fixed-window rate limiter (lib/rate_limiter.js) used to
4
- // gate the L402 invoice-creating path. Offline, no network, no timers waited on.
5
-
6
- const assert = require("assert");
7
- const { createRateLimiter } = require("../lib/rate_limiter");
8
-
9
- function main() {
10
- // Per-key ceiling: first `max` hits allowed, the next denied with scope "key".
11
- const rl = createRateLimiter({ windowMs: 60000, max: 3, maxGlobal: 100 });
12
- assert.strictEqual(rl.check("1.1.1.1").allowed, true);
13
- assert.strictEqual(rl.check("1.1.1.1").allowed, true);
14
- const third = rl.check("1.1.1.1");
15
- assert.strictEqual(third.allowed, true);
16
- assert.strictEqual(third.remaining, 0);
17
- const denied = rl.check("1.1.1.1");
18
- assert.strictEqual(denied.allowed, false);
19
- assert.strictEqual(denied.scope, "key");
20
- assert.ok(denied.retryAfterMs > 0 && denied.retryAfterMs <= 60000);
21
-
22
- // A different key is independent (not affected by another key's exhaustion).
23
- assert.strictEqual(rl.check("2.2.2.2").allowed, true);
24
-
25
- // Denied hits are not counted, so they cannot push the reset further out or
26
- // starve a key beyond its window (no self-inflicted lockout extension).
27
- assert.strictEqual(rl.check("1.1.1.1").allowed, false);
28
-
29
- // Global ceiling: once the global budget is spent, a fresh key is still denied
30
- // with scope "global" (maxGlobal >= max always, so set them equal to exercise it
31
- // across distinct keys before any single key is exhausted).
32
- const g = createRateLimiter({ windowMs: 60000, max: 2, maxGlobal: 2 });
33
- assert.strictEqual(g.check("a").allowed, true); // global 1, key a 1
34
- assert.strictEqual(g.check("b").allowed, true); // global 2, key b 1
35
- const gDenied = g.check("c"); // global budget spent, key c untouched
36
- assert.strictEqual(gDenied.allowed, false);
37
- assert.strictEqual(gDenied.scope, "global");
38
-
39
- // reset() clears all buckets.
40
- g.reset();
41
- assert.strictEqual(g.check("c").allowed, true);
42
-
43
- // Config is clamped to sane floors.
44
- const clamped = createRateLimiter({ windowMs: 1, max: 0, maxGlobal: 0 });
45
- assert.ok(clamped.config.windowMs >= 1000);
46
- assert.ok(clamped.config.max >= 1);
47
- assert.ok(clamped.config.maxGlobal >= clamped.config.max);
48
-
49
- console.log("verify_rate_limiter: all rate limiter checks passed");
50
- }
51
-
52
- main();
1
+ "use strict";
2
+
3
+ // Verifies the in-memory fixed-window rate limiter (lib/rate_limiter.js) used to
4
+ // gate the L402 invoice-creating path. Offline, no network, no timers waited on.
5
+
6
+ const assert = require("assert");
7
+ const { createRateLimiter } = require("../lib/rate_limiter");
8
+
9
+ function main() {
10
+ // Per-key ceiling: first `max` hits allowed, the next denied with scope "key".
11
+ const rl = createRateLimiter({ windowMs: 60000, max: 3, maxGlobal: 100 });
12
+ assert.strictEqual(rl.check("1.1.1.1").allowed, true);
13
+ assert.strictEqual(rl.check("1.1.1.1").allowed, true);
14
+ const third = rl.check("1.1.1.1");
15
+ assert.strictEqual(third.allowed, true);
16
+ assert.strictEqual(third.remaining, 0);
17
+ const denied = rl.check("1.1.1.1");
18
+ assert.strictEqual(denied.allowed, false);
19
+ assert.strictEqual(denied.scope, "key");
20
+ assert.ok(denied.retryAfterMs > 0 && denied.retryAfterMs <= 60000);
21
+
22
+ // A different key is independent (not affected by another key's exhaustion).
23
+ assert.strictEqual(rl.check("2.2.2.2").allowed, true);
24
+
25
+ // Denied hits are not counted, so they cannot push the reset further out or
26
+ // starve a key beyond its window (no self-inflicted lockout extension).
27
+ assert.strictEqual(rl.check("1.1.1.1").allowed, false);
28
+
29
+ // Global ceiling: once the global budget is spent, a fresh key is still denied
30
+ // with scope "global" (maxGlobal >= max always, so set them equal to exercise it
31
+ // across distinct keys before any single key is exhausted).
32
+ const g = createRateLimiter({ windowMs: 60000, max: 2, maxGlobal: 2 });
33
+ assert.strictEqual(g.check("a").allowed, true); // global 1, key a 1
34
+ assert.strictEqual(g.check("b").allowed, true); // global 2, key b 1
35
+ const gDenied = g.check("c"); // global budget spent, key c untouched
36
+ assert.strictEqual(gDenied.allowed, false);
37
+ assert.strictEqual(gDenied.scope, "global");
38
+
39
+ // reset() clears all buckets.
40
+ g.reset();
41
+ assert.strictEqual(g.check("c").allowed, true);
42
+
43
+ // Config is clamped to sane floors.
44
+ const clamped = createRateLimiter({ windowMs: 1, max: 0, maxGlobal: 0 });
45
+ assert.ok(clamped.config.windowMs >= 1000);
46
+ assert.ok(clamped.config.max >= 1);
47
+ assert.ok(clamped.config.maxGlobal >= clamped.config.max);
48
+
49
+ console.log("verify_rate_limiter: all rate limiter checks passed");
50
+ }
51
+
52
+ main();