xytara 3.1.13 → 3.1.14
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/.env.example +8 -1
- package/BSV_TERANODE_PLATFORM_DISCONTINUITY_RUNBOOK.md +126 -126
- package/RELEASE_NOTES.md +6 -4
- package/lib/a2a_rpc.js +280 -280
- package/lib/account_auth.js +585 -585
- package/lib/agent_identity.js +162 -162
- package/lib/bolt_adapter.js +187 -187
- package/lib/bolt_payment_front.js +224 -224
- package/lib/checkout_processor_adapter.js +217 -217
- package/lib/command_flow.js +722 -722
- package/lib/commerce_artifacts.js +591 -591
- package/lib/commerce_bridge.js +274 -274
- package/lib/commerce_cases.js +399 -399
- package/lib/commerce_client.js +7694 -7694
- package/lib/commerce_quotes.js +29 -0
- package/lib/commerce_runtime.js +322 -322
- package/lib/execution_dedupe.js +86 -86
- package/lib/external_credit_bridge.js +142 -142
- package/lib/http_transport.js +81 -81
- package/lib/l402_adapter.js +241 -241
- package/lib/l402_payment_front.js +265 -265
- package/lib/mpp_adapter.js +237 -237
- package/lib/payment_config.js +253 -253
- package/lib/payment_verification.js +357 -357
- package/lib/provider_payout_execution.js +155 -155
- package/lib/provider_registry.js +842 -842
- package/lib/rate_limiter.js +81 -81
- package/lib/runtime_state_store.js +491 -369
- package/lib/settlement_bsv_live.js +600 -600
- package/lib/settlement_bsv_teranode.js +117 -117
- package/lib/stripe_mpp_payment_front.js +258 -258
- package/lib/x402_facilitator.js +191 -191
- package/package.json +5 -3
- package/scripts/state_backup_offsite.js +196 -0
- package/scripts/verify_a2a_rpc.js +105 -105
- package/scripts/verify_agent_identity.js +153 -153
- package/scripts/verify_all.js +1 -1
- package/scripts/verify_bolt_adapter.js +128 -128
- package/scripts/verify_cdp_facilitator.js +110 -110
- package/scripts/verify_checkout_processor.js +109 -109
- package/scripts/verify_commerce_bridge.js +177 -177
- package/scripts/verify_execution_dedupe.js +189 -189
- package/scripts/verify_execution_surfacing.js +76 -76
- package/scripts/verify_final_activation.js +191 -191
- package/scripts/verify_l402_adapter.js +86 -86
- package/scripts/verify_mpp_adapter.js +128 -128
- package/scripts/verify_production_readiness.js +268 -268
- package/scripts/verify_rate_limiter.js +52 -52
- package/scripts/verify_refund_credit.js +106 -106
- package/scripts/verify_release_candidate.js +81 -81
- package/scripts/verify_runtime_state_guard.js +117 -0
- package/scripts/verify_service.js +15421 -15421
- package/scripts/verify_verify_execute_settle.js +208 -208
- package/scripts/verify_x402_facilitator.js +286 -286
- package/server.js +11976 -11974
package/lib/rate_limiter.js
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Minimal in-memory fixed-window rate limiter. Dependency-free.
|
|
4
|
-
//
|
|
5
|
-
// Used to gate expensive unauthenticated work (e.g. L402 invoice creation, which
|
|
6
|
-
// makes an outbound call to a Lightning provider on every UNPAID request) so a
|
|
7
|
-
// flood cannot amplify into unbounded cost. Two independent ceilings apply:
|
|
8
|
-
// - per key (typically client IP): `max` hits per `windowMs`
|
|
9
|
-
// - global (all keys combined): `maxGlobal` hits per `windowMs`
|
|
10
|
-
// A hit counts against both; exceeding either denies the request.
|
|
11
|
-
//
|
|
12
|
-
// Scope: single process. Horizontally-scaled deployments should front this with a
|
|
13
|
-
// shared store (Redis) or an edge/proxy limiter; this module is the in-process
|
|
14
|
-
// floor, not a distributed guarantee. Callers should still deploy behind the
|
|
15
|
-
// platform's own rate limiting for defense in depth.
|
|
16
|
-
|
|
17
|
-
function now() {
|
|
18
|
-
return Date.now();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function createRateLimiter(options) {
|
|
22
|
-
const opts = options || {};
|
|
23
|
-
const windowMs = Math.max(1000, Number(opts.windowMs) || 60000);
|
|
24
|
-
const max = Math.max(1, Number(opts.max) || 30);
|
|
25
|
-
const maxGlobal = Math.max(max, Number(opts.maxGlobal) || max * 20);
|
|
26
|
-
// Map<key, { count, resetAt }>
|
|
27
|
-
const buckets = new Map();
|
|
28
|
-
let global = { count: 0, resetAt: now() + windowMs };
|
|
29
|
-
let lastSweep = now();
|
|
30
|
-
|
|
31
|
-
function sweep(t) {
|
|
32
|
-
// Opportunistic cleanup so the Map cannot grow without bound.
|
|
33
|
-
if (t - lastSweep < windowMs) return;
|
|
34
|
-
lastSweep = t;
|
|
35
|
-
for (const [key, bucket] of buckets) {
|
|
36
|
-
if (bucket.resetAt <= t) buckets.delete(key);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function rollGlobal(t) {
|
|
41
|
-
if (global.resetAt <= t) global = { count: 0, resetAt: t + windowMs };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Record one hit for `key` and report whether it is allowed. The hit is only
|
|
45
|
-
// counted when allowed, so a denied caller does not push its own reset further
|
|
46
|
-
// out (no self-inflicted lockout extension).
|
|
47
|
-
function check(key) {
|
|
48
|
-
const t = now();
|
|
49
|
-
sweep(t);
|
|
50
|
-
rollGlobal(t);
|
|
51
|
-
const k = String(key == null ? "" : key);
|
|
52
|
-
let bucket = buckets.get(k);
|
|
53
|
-
if (!bucket || bucket.resetAt <= t) {
|
|
54
|
-
bucket = { count: 0, resetAt: t + windowMs };
|
|
55
|
-
buckets.set(k, bucket);
|
|
56
|
-
}
|
|
57
|
-
if (global.count >= maxGlobal) {
|
|
58
|
-
return { allowed: false, scope: "global", remaining: 0, retryAfterMs: Math.max(0, global.resetAt - t) };
|
|
59
|
-
}
|
|
60
|
-
if (bucket.count >= max) {
|
|
61
|
-
return { allowed: false, scope: "key", remaining: 0, retryAfterMs: Math.max(0, bucket.resetAt - t) };
|
|
62
|
-
}
|
|
63
|
-
bucket.count += 1;
|
|
64
|
-
global.count += 1;
|
|
65
|
-
return {
|
|
66
|
-
allowed: true,
|
|
67
|
-
scope: null,
|
|
68
|
-
remaining: Math.max(0, max - bucket.count),
|
|
69
|
-
retryAfterMs: Math.max(0, bucket.resetAt - t)
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function reset() {
|
|
74
|
-
buckets.clear();
|
|
75
|
-
global = { count: 0, resetAt: now() + windowMs };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return { check, reset, config: { windowMs, max, maxGlobal } };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
module.exports = { createRateLimiter };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Minimal in-memory fixed-window rate limiter. Dependency-free.
|
|
4
|
+
//
|
|
5
|
+
// Used to gate expensive unauthenticated work (e.g. L402 invoice creation, which
|
|
6
|
+
// makes an outbound call to a Lightning provider on every UNPAID request) so a
|
|
7
|
+
// flood cannot amplify into unbounded cost. Two independent ceilings apply:
|
|
8
|
+
// - per key (typically client IP): `max` hits per `windowMs`
|
|
9
|
+
// - global (all keys combined): `maxGlobal` hits per `windowMs`
|
|
10
|
+
// A hit counts against both; exceeding either denies the request.
|
|
11
|
+
//
|
|
12
|
+
// Scope: single process. Horizontally-scaled deployments should front this with a
|
|
13
|
+
// shared store (Redis) or an edge/proxy limiter; this module is the in-process
|
|
14
|
+
// floor, not a distributed guarantee. Callers should still deploy behind the
|
|
15
|
+
// platform's own rate limiting for defense in depth.
|
|
16
|
+
|
|
17
|
+
function now() {
|
|
18
|
+
return Date.now();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createRateLimiter(options) {
|
|
22
|
+
const opts = options || {};
|
|
23
|
+
const windowMs = Math.max(1000, Number(opts.windowMs) || 60000);
|
|
24
|
+
const max = Math.max(1, Number(opts.max) || 30);
|
|
25
|
+
const maxGlobal = Math.max(max, Number(opts.maxGlobal) || max * 20);
|
|
26
|
+
// Map<key, { count, resetAt }>
|
|
27
|
+
const buckets = new Map();
|
|
28
|
+
let global = { count: 0, resetAt: now() + windowMs };
|
|
29
|
+
let lastSweep = now();
|
|
30
|
+
|
|
31
|
+
function sweep(t) {
|
|
32
|
+
// Opportunistic cleanup so the Map cannot grow without bound.
|
|
33
|
+
if (t - lastSweep < windowMs) return;
|
|
34
|
+
lastSweep = t;
|
|
35
|
+
for (const [key, bucket] of buckets) {
|
|
36
|
+
if (bucket.resetAt <= t) buckets.delete(key);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function rollGlobal(t) {
|
|
41
|
+
if (global.resetAt <= t) global = { count: 0, resetAt: t + windowMs };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Record one hit for `key` and report whether it is allowed. The hit is only
|
|
45
|
+
// counted when allowed, so a denied caller does not push its own reset further
|
|
46
|
+
// out (no self-inflicted lockout extension).
|
|
47
|
+
function check(key) {
|
|
48
|
+
const t = now();
|
|
49
|
+
sweep(t);
|
|
50
|
+
rollGlobal(t);
|
|
51
|
+
const k = String(key == null ? "" : key);
|
|
52
|
+
let bucket = buckets.get(k);
|
|
53
|
+
if (!bucket || bucket.resetAt <= t) {
|
|
54
|
+
bucket = { count: 0, resetAt: t + windowMs };
|
|
55
|
+
buckets.set(k, bucket);
|
|
56
|
+
}
|
|
57
|
+
if (global.count >= maxGlobal) {
|
|
58
|
+
return { allowed: false, scope: "global", remaining: 0, retryAfterMs: Math.max(0, global.resetAt - t) };
|
|
59
|
+
}
|
|
60
|
+
if (bucket.count >= max) {
|
|
61
|
+
return { allowed: false, scope: "key", remaining: 0, retryAfterMs: Math.max(0, bucket.resetAt - t) };
|
|
62
|
+
}
|
|
63
|
+
bucket.count += 1;
|
|
64
|
+
global.count += 1;
|
|
65
|
+
return {
|
|
66
|
+
allowed: true,
|
|
67
|
+
scope: null,
|
|
68
|
+
remaining: Math.max(0, max - bucket.count),
|
|
69
|
+
retryAfterMs: Math.max(0, bucket.resetAt - t)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function reset() {
|
|
74
|
+
buckets.clear();
|
|
75
|
+
global = { count: 0, resetAt: now() + windowMs };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { check, reset, config: { windowMs, max, maxGlobal } };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { createRateLimiter };
|