blockintel-gate-sdk 0.3.1 → 0.3.2
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 +5 -3
- package/dist/index.cjs +552 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +200 -4
- package/dist/index.d.ts +200 -4
- package/dist/index.js +552 -67
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,64 +2,48 @@ import { v4 } from 'uuid';
|
|
|
2
2
|
import { SignCommand } from '@aws-sdk/client-kms';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
7
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
8
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
9
|
}) : x)(function(x) {
|
|
8
10
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
11
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
12
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const key = await crypto.subtle.importKey(
|
|
19
|
-
"raw",
|
|
20
|
-
keyData,
|
|
21
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
22
|
-
false,
|
|
23
|
-
["sign"]
|
|
24
|
-
);
|
|
25
|
-
const signature = await crypto.subtle.sign("HMAC", key, messageData);
|
|
26
|
-
const hashArray = Array.from(new Uint8Array(signature));
|
|
27
|
-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
28
|
-
}
|
|
29
|
-
if (typeof __require !== "undefined") {
|
|
30
|
-
const crypto2 = __require("crypto");
|
|
31
|
-
const hmac = crypto2.createHmac("sha256", secret);
|
|
32
|
-
hmac.update(message, "utf8");
|
|
33
|
-
return hmac.digest("hex");
|
|
34
|
-
}
|
|
35
|
-
throw new Error("HMAC-SHA256 not available in this environment");
|
|
36
|
-
}
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
37
20
|
|
|
38
21
|
// src/utils/canonicalJson.ts
|
|
22
|
+
var canonicalJson_exports = {};
|
|
23
|
+
__export(canonicalJson_exports, {
|
|
24
|
+
canonicalizeJson: () => canonicalizeJson,
|
|
25
|
+
sha256Hex: () => sha256Hex
|
|
26
|
+
});
|
|
39
27
|
function canonicalizeJson(obj) {
|
|
40
28
|
if (obj === null || obj === void 0) {
|
|
41
29
|
return "null";
|
|
42
30
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const value = obj[key];
|
|
57
|
-
const canonicalValue = canonicalizeJson(value);
|
|
58
|
-
return `${JSON.stringify(key)}:${canonicalValue}`;
|
|
59
|
-
});
|
|
60
|
-
return `{${pairs.join(",")}}`;
|
|
31
|
+
const cloned = JSON.parse(JSON.stringify(obj));
|
|
32
|
+
function sortKeys(item) {
|
|
33
|
+
if (Array.isArray(item)) {
|
|
34
|
+
return item.map(sortKeys);
|
|
35
|
+
}
|
|
36
|
+
if (item !== null && typeof item === "object") {
|
|
37
|
+
const sorted2 = {};
|
|
38
|
+
Object.keys(item).sort().forEach((key) => {
|
|
39
|
+
sorted2[key] = sortKeys(item[key]);
|
|
40
|
+
});
|
|
41
|
+
return sorted2;
|
|
42
|
+
}
|
|
43
|
+
return item;
|
|
61
44
|
}
|
|
62
|
-
|
|
45
|
+
const sorted = sortKeys(cloned);
|
|
46
|
+
return JSON.stringify(sorted);
|
|
63
47
|
}
|
|
64
48
|
async function sha256Hex(input) {
|
|
65
49
|
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
@@ -75,14 +59,53 @@ async function sha256Hex(input) {
|
|
|
75
59
|
}
|
|
76
60
|
throw new Error("SHA-256 not available in this environment");
|
|
77
61
|
}
|
|
62
|
+
var init_canonicalJson = __esm({
|
|
63
|
+
"src/utils/canonicalJson.ts"() {
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/utils/crypto.ts
|
|
68
|
+
async function hmacSha256(secret, message) {
|
|
69
|
+
if (typeof __require !== "undefined") {
|
|
70
|
+
const crypto2 = __require("crypto");
|
|
71
|
+
const hmac = crypto2.createHmac("sha256", secret);
|
|
72
|
+
hmac.update(message, "utf8");
|
|
73
|
+
const signatureHex = hmac.digest("hex");
|
|
74
|
+
console.error("[HMAC CRYPTO DEBUG] Signature computation:", JSON.stringify({
|
|
75
|
+
secretLength: secret.length,
|
|
76
|
+
messageLength: message.length,
|
|
77
|
+
messagePreview: message.substring(0, 200) + "...",
|
|
78
|
+
signatureLength: signatureHex.length,
|
|
79
|
+
signaturePreview: signatureHex.substring(0, 16) + "..."
|
|
80
|
+
}, null, 2));
|
|
81
|
+
return signatureHex;
|
|
82
|
+
}
|
|
83
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
84
|
+
const encoder = new TextEncoder();
|
|
85
|
+
const keyData = encoder.encode(secret);
|
|
86
|
+
const messageData = encoder.encode(message);
|
|
87
|
+
const key = await crypto.subtle.importKey(
|
|
88
|
+
"raw",
|
|
89
|
+
keyData,
|
|
90
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
91
|
+
false,
|
|
92
|
+
["sign"]
|
|
93
|
+
);
|
|
94
|
+
const signature = await crypto.subtle.sign("HMAC", key, messageData);
|
|
95
|
+
const hashArray = Array.from(new Uint8Array(signature));
|
|
96
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
97
|
+
}
|
|
98
|
+
throw new Error("HMAC-SHA256 not available in this environment");
|
|
99
|
+
}
|
|
78
100
|
|
|
79
101
|
// src/auth/HmacSigner.ts
|
|
102
|
+
init_canonicalJson();
|
|
80
103
|
var HmacSigner = class {
|
|
81
104
|
keyId;
|
|
82
105
|
secret;
|
|
83
106
|
constructor(config) {
|
|
84
107
|
this.keyId = config.keyId;
|
|
85
|
-
this.secret = config.secret;
|
|
108
|
+
this.secret = config.secret.trim();
|
|
86
109
|
if (!this.secret || this.secret.length === 0) {
|
|
87
110
|
throw new Error("HMAC secret cannot be empty");
|
|
88
111
|
}
|
|
@@ -105,7 +128,26 @@ var HmacSigner = class {
|
|
|
105
128
|
// Used as nonce in canonical string
|
|
106
129
|
bodyHash
|
|
107
130
|
].join("\n");
|
|
131
|
+
console.error("[HMAC SIGNER DEBUG] Canonical request string:", JSON.stringify({
|
|
132
|
+
method: method.toUpperCase(),
|
|
133
|
+
path,
|
|
134
|
+
tenantId,
|
|
135
|
+
keyId: this.keyId,
|
|
136
|
+
timestampMs: String(timestampMs),
|
|
137
|
+
requestId,
|
|
138
|
+
bodyHash,
|
|
139
|
+
signingStringLength: signingString.length,
|
|
140
|
+
signingStringPreview: signingString.substring(0, 200) + "...",
|
|
141
|
+
bodyJsonLength: bodyJson.length,
|
|
142
|
+
bodyJsonPreview: bodyJson.substring(0, 200) + "..."
|
|
143
|
+
}, null, 2));
|
|
108
144
|
const signature = await hmacSha256(this.secret, signingString);
|
|
145
|
+
console.error("[HMAC SIGNER DEBUG] Signature computed:", JSON.stringify({
|
|
146
|
+
signatureLength: signature.length,
|
|
147
|
+
signaturePreview: signature.substring(0, 16) + "...",
|
|
148
|
+
secretLength: this.secret.length,
|
|
149
|
+
secretPreview: this.secret.substring(0, 4) + "..." + this.secret.substring(this.secret.length - 4)
|
|
150
|
+
}, null, 2));
|
|
109
151
|
return {
|
|
110
152
|
"X-GATE-TENANT-ID": tenantId,
|
|
111
153
|
"X-GATE-KEY-ID": this.keyId,
|
|
@@ -154,6 +196,10 @@ var GateErrorCode = /* @__PURE__ */ ((GateErrorCode2) => {
|
|
|
154
196
|
GateErrorCode2["BLOCKED"] = "BLOCKED";
|
|
155
197
|
GateErrorCode2["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
|
|
156
198
|
GateErrorCode2["AUTH_ERROR"] = "AUTH_ERROR";
|
|
199
|
+
GateErrorCode2["HEARTBEAT_MISSING"] = "HEARTBEAT_MISSING";
|
|
200
|
+
GateErrorCode2["HEARTBEAT_EXPIRED"] = "HEARTBEAT_EXPIRED";
|
|
201
|
+
GateErrorCode2["HEARTBEAT_INVALID"] = "HEARTBEAT_INVALID";
|
|
202
|
+
GateErrorCode2["HEARTBEAT_MISMATCH"] = "HEARTBEAT_MISMATCH";
|
|
157
203
|
return GateErrorCode2;
|
|
158
204
|
})(GateErrorCode || {});
|
|
159
205
|
var GateError = class extends Error {
|
|
@@ -335,21 +381,55 @@ var HttpClient = class {
|
|
|
335
381
|
const url = `${this.baseUrl}${path}`;
|
|
336
382
|
const controller = new AbortController();
|
|
337
383
|
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
384
|
+
let requestDetailsForLogging = null;
|
|
385
|
+
let requestDetailsSet = false;
|
|
338
386
|
try {
|
|
339
387
|
const response = await retryWithBackoff(
|
|
340
388
|
async () => {
|
|
389
|
+
const requestHeaders = {};
|
|
390
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
391
|
+
requestHeaders[key] = String(value);
|
|
392
|
+
}
|
|
393
|
+
requestHeaders["User-Agent"] = this.userAgent;
|
|
394
|
+
requestHeaders["Content-Type"] = "application/json";
|
|
341
395
|
const fetchOptions = {
|
|
342
396
|
method,
|
|
343
|
-
headers:
|
|
344
|
-
...headers,
|
|
345
|
-
"User-Agent": this.userAgent,
|
|
346
|
-
"Content-Type": "application/json"
|
|
347
|
-
},
|
|
397
|
+
headers: requestHeaders,
|
|
348
398
|
signal: controller.signal
|
|
349
399
|
};
|
|
350
400
|
if (body) {
|
|
351
|
-
|
|
401
|
+
if (body.__canonicalJson) {
|
|
402
|
+
fetchOptions.body = body.__canonicalJson;
|
|
403
|
+
delete body.__canonicalJson;
|
|
404
|
+
} else {
|
|
405
|
+
fetchOptions.body = JSON.stringify(body);
|
|
406
|
+
}
|
|
352
407
|
}
|
|
408
|
+
const logHeaders = {};
|
|
409
|
+
if (fetchOptions.headers) {
|
|
410
|
+
Object.entries(fetchOptions.headers).forEach(([key, value]) => {
|
|
411
|
+
if (key.toLowerCase().includes("signature") || key.toLowerCase().includes("secret")) {
|
|
412
|
+
logHeaders[key] = String(value).substring(0, 8) + "...";
|
|
413
|
+
} else {
|
|
414
|
+
logHeaders[key] = String(value);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
const bodyStr = typeof fetchOptions.body === "string" ? fetchOptions.body : null;
|
|
419
|
+
const details = {
|
|
420
|
+
headers: logHeaders,
|
|
421
|
+
bodyLength: bodyStr ? bodyStr.length : 0,
|
|
422
|
+
bodyPreview: bodyStr ? bodyStr.substring(0, 300) : null
|
|
423
|
+
};
|
|
424
|
+
requestDetailsForLogging = details;
|
|
425
|
+
requestDetailsSet = true;
|
|
426
|
+
console.error("[HTTP CLIENT DEBUG] Sending request:", JSON.stringify({
|
|
427
|
+
url,
|
|
428
|
+
method,
|
|
429
|
+
headers: logHeaders,
|
|
430
|
+
bodyLength: requestDetailsForLogging.bodyLength,
|
|
431
|
+
bodyPreview: requestDetailsForLogging.bodyPreview
|
|
432
|
+
}, null, 2));
|
|
353
433
|
const res = await fetch(url, fetchOptions);
|
|
354
434
|
if (!res.ok && isRetryableStatus(res.status)) {
|
|
355
435
|
throw res;
|
|
@@ -367,10 +447,26 @@ var HttpClient = class {
|
|
|
367
447
|
clearTimeout(timeoutId);
|
|
368
448
|
let data;
|
|
369
449
|
const contentType = response.headers.get("content-type");
|
|
450
|
+
console.error("[HTTP CLIENT DEBUG] Response received:", JSON.stringify({
|
|
451
|
+
status: response.status,
|
|
452
|
+
ok: response.ok,
|
|
453
|
+
statusText: response.statusText,
|
|
454
|
+
contentType,
|
|
455
|
+
url: response.url
|
|
456
|
+
}, null, 2));
|
|
370
457
|
if (contentType && contentType.includes("application/json")) {
|
|
371
458
|
try {
|
|
372
|
-
|
|
459
|
+
const jsonText = await response.text();
|
|
460
|
+
console.error("[HTTP CLIENT DEBUG] Response body (first 500 chars):", jsonText.substring(0, 500));
|
|
461
|
+
data = JSON.parse(jsonText);
|
|
462
|
+
console.error("[HTTP CLIENT DEBUG] Parsed JSON:", JSON.stringify({
|
|
463
|
+
hasSuccess: typeof data?.success !== "undefined",
|
|
464
|
+
success: data?.success,
|
|
465
|
+
hasData: typeof data?.data !== "undefined",
|
|
466
|
+
hasError: typeof data?.error !== "undefined"
|
|
467
|
+
}, null, 2));
|
|
373
468
|
} catch (parseError) {
|
|
469
|
+
console.error("[HTTP CLIENT DEBUG] JSON parse error:", parseError);
|
|
374
470
|
throw new GateError(
|
|
375
471
|
"INVALID_RESPONSE" /* INVALID_RESPONSE */,
|
|
376
472
|
"Failed to parse JSON response",
|
|
@@ -394,6 +490,32 @@ var HttpClient = class {
|
|
|
394
490
|
);
|
|
395
491
|
}
|
|
396
492
|
if (!response.ok) {
|
|
493
|
+
const responseHeaders = {};
|
|
494
|
+
response.headers.forEach((value, key) => {
|
|
495
|
+
responseHeaders[key] = value;
|
|
496
|
+
});
|
|
497
|
+
if (response.status === 401) {
|
|
498
|
+
console.error("[HTTP CLIENT DEBUG] 401 UNAUTHORIZED - Full request details:", JSON.stringify({
|
|
499
|
+
status: response.status,
|
|
500
|
+
statusText: response.statusText,
|
|
501
|
+
url: response.url,
|
|
502
|
+
requestMethod: method,
|
|
503
|
+
requestPath: path,
|
|
504
|
+
requestHeaders: requestDetailsForLogging ? requestDetailsForLogging.headers : {},
|
|
505
|
+
responseHeaders,
|
|
506
|
+
responseData: data,
|
|
507
|
+
bodyLength: requestDetailsForLogging ? requestDetailsForLogging.bodyLength : 0,
|
|
508
|
+
bodyPreview: requestDetailsForLogging ? requestDetailsForLogging.bodyPreview : null
|
|
509
|
+
}, null, 2));
|
|
510
|
+
} else {
|
|
511
|
+
console.error("[HTTP CLIENT DEBUG] Response not OK:", JSON.stringify({
|
|
512
|
+
status: response.status,
|
|
513
|
+
statusText: response.statusText,
|
|
514
|
+
url: response.url,
|
|
515
|
+
headers: responseHeaders,
|
|
516
|
+
data
|
|
517
|
+
}, null, 2));
|
|
518
|
+
}
|
|
397
519
|
const errorCode = this.statusToErrorCode(response.status);
|
|
398
520
|
const correlationId = response.headers.get("X-Correlation-ID") ?? void 0;
|
|
399
521
|
throw new GateError(errorCode, `HTTP ${response.status}: ${response.statusText}`, {
|
|
@@ -403,6 +525,7 @@ var HttpClient = class {
|
|
|
403
525
|
details: data
|
|
404
526
|
});
|
|
405
527
|
}
|
|
528
|
+
console.error("[HTTP CLIENT DEBUG] Response OK, returning data");
|
|
406
529
|
return data;
|
|
407
530
|
} catch (error) {
|
|
408
531
|
clearTimeout(timeoutId);
|
|
@@ -712,6 +835,10 @@ var MetricsCollector = class {
|
|
|
712
835
|
timeoutsTotal = 0;
|
|
713
836
|
errorsTotal = 0;
|
|
714
837
|
circuitBreakerOpenTotal = 0;
|
|
838
|
+
wouldBlockTotal = 0;
|
|
839
|
+
// Shadow mode would-block count
|
|
840
|
+
failOpenTotal = 0;
|
|
841
|
+
// Fail-open count
|
|
715
842
|
latencyMs = [];
|
|
716
843
|
maxSamples = 1e3;
|
|
717
844
|
// Keep last 1000 samples
|
|
@@ -727,6 +854,12 @@ var MetricsCollector = class {
|
|
|
727
854
|
this.blockedTotal++;
|
|
728
855
|
} else if (decision === "REQUIRE_STEP_UP") {
|
|
729
856
|
this.stepupTotal++;
|
|
857
|
+
} else if (decision === "WOULD_BLOCK") {
|
|
858
|
+
this.wouldBlockTotal++;
|
|
859
|
+
this.allowedTotal++;
|
|
860
|
+
} else if (decision === "FAIL_OPEN") {
|
|
861
|
+
this.failOpenTotal++;
|
|
862
|
+
this.allowedTotal++;
|
|
730
863
|
}
|
|
731
864
|
this.latencyMs.push(latencyMs);
|
|
732
865
|
if (this.latencyMs.length > this.maxSamples) {
|
|
@@ -768,6 +901,8 @@ var MetricsCollector = class {
|
|
|
768
901
|
timeoutsTotal: this.timeoutsTotal,
|
|
769
902
|
errorsTotal: this.errorsTotal,
|
|
770
903
|
circuitBreakerOpenTotal: this.circuitBreakerOpenTotal,
|
|
904
|
+
wouldBlockTotal: this.wouldBlockTotal,
|
|
905
|
+
failOpenTotal: this.failOpenTotal,
|
|
771
906
|
latencyMs: [...this.latencyMs]
|
|
772
907
|
// Copy array
|
|
773
908
|
};
|
|
@@ -802,6 +937,8 @@ var MetricsCollector = class {
|
|
|
802
937
|
this.timeoutsTotal = 0;
|
|
803
938
|
this.errorsTotal = 0;
|
|
804
939
|
this.circuitBreakerOpenTotal = 0;
|
|
940
|
+
this.wouldBlockTotal = 0;
|
|
941
|
+
this.failOpenTotal = 0;
|
|
805
942
|
this.latencyMs = [];
|
|
806
943
|
}
|
|
807
944
|
};
|
|
@@ -854,10 +991,25 @@ function defaultExtractTxIntent(command) {
|
|
|
854
991
|
async function handleSignCommand(command, originalClient, gateClient, options) {
|
|
855
992
|
const txIntent = options.extractTxIntent(command);
|
|
856
993
|
const signerId = command.input?.KeyId ?? command.KeyId ?? "unknown";
|
|
994
|
+
gateClient.heartbeatManager.updateSignerId(signerId);
|
|
995
|
+
const heartbeatToken = gateClient.heartbeatManager.getToken();
|
|
996
|
+
if (!heartbeatToken) {
|
|
997
|
+
throw new BlockIntelBlockedError(
|
|
998
|
+
"HEARTBEAT_MISSING",
|
|
999
|
+
void 0,
|
|
1000
|
+
// receiptId
|
|
1001
|
+
void 0,
|
|
1002
|
+
// correlationId
|
|
1003
|
+
void 0
|
|
1004
|
+
// requestId
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
857
1007
|
const signingContext = {
|
|
858
1008
|
signerId,
|
|
859
|
-
actorPrincipal: "kms-signer"
|
|
1009
|
+
actorPrincipal: "kms-signer",
|
|
860
1010
|
// Default - can be customized via extractTxIntent
|
|
1011
|
+
heartbeatToken
|
|
1012
|
+
// Attach heartbeat token
|
|
861
1013
|
};
|
|
862
1014
|
try {
|
|
863
1015
|
const decision = await gateClient.evaluate({
|
|
@@ -922,6 +1074,177 @@ var ProvenanceProvider = class {
|
|
|
922
1074
|
return !!(process.env.GATE_CALLER_REPO || process.env.GATE_CALLER_WORKFLOW || process.env.GATE_ATTESTATION_VALID);
|
|
923
1075
|
}
|
|
924
1076
|
};
|
|
1077
|
+
var HeartbeatManager = class {
|
|
1078
|
+
httpClient;
|
|
1079
|
+
tenantId;
|
|
1080
|
+
signerId;
|
|
1081
|
+
environment;
|
|
1082
|
+
baseRefreshIntervalSeconds;
|
|
1083
|
+
clientInstanceId;
|
|
1084
|
+
// Unique per process
|
|
1085
|
+
sdkVersion;
|
|
1086
|
+
// SDK version for tracking
|
|
1087
|
+
currentToken = null;
|
|
1088
|
+
refreshTimer = null;
|
|
1089
|
+
started = false;
|
|
1090
|
+
consecutiveFailures = 0;
|
|
1091
|
+
maxBackoffSeconds = 30;
|
|
1092
|
+
// Maximum backoff interval
|
|
1093
|
+
constructor(options) {
|
|
1094
|
+
this.httpClient = options.httpClient;
|
|
1095
|
+
this.tenantId = options.tenantId;
|
|
1096
|
+
this.signerId = options.signerId;
|
|
1097
|
+
this.environment = options.environment ?? "prod";
|
|
1098
|
+
this.baseRefreshIntervalSeconds = options.refreshIntervalSeconds ?? 10;
|
|
1099
|
+
this.clientInstanceId = options.clientInstanceId || v4();
|
|
1100
|
+
this.sdkVersion = options.sdkVersion || "1.0.0";
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Start background heartbeat refresher
|
|
1104
|
+
*/
|
|
1105
|
+
start() {
|
|
1106
|
+
if (this.started) {
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
this.started = true;
|
|
1110
|
+
this.acquireHeartbeat().catch((error) => {
|
|
1111
|
+
console.error("[HEARTBEAT] Failed to acquire initial heartbeat:", error);
|
|
1112
|
+
});
|
|
1113
|
+
this.scheduleNextRefresh();
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Schedule next refresh with jitter and backoff
|
|
1117
|
+
*/
|
|
1118
|
+
scheduleNextRefresh() {
|
|
1119
|
+
if (!this.started) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const baseInterval = this.baseRefreshIntervalSeconds * 1e3;
|
|
1123
|
+
const jitter = Math.random() * 2e3;
|
|
1124
|
+
const backoff = this.calculateBackoff();
|
|
1125
|
+
const interval = baseInterval + jitter + backoff;
|
|
1126
|
+
this.refreshTimer = setTimeout(() => {
|
|
1127
|
+
this.acquireHeartbeat().then(() => {
|
|
1128
|
+
this.consecutiveFailures = 0;
|
|
1129
|
+
this.scheduleNextRefresh();
|
|
1130
|
+
}).catch((error) => {
|
|
1131
|
+
this.consecutiveFailures++;
|
|
1132
|
+
console.error("[HEARTBEAT] Refresh failed (will retry):", error);
|
|
1133
|
+
this.scheduleNextRefresh();
|
|
1134
|
+
});
|
|
1135
|
+
}, interval);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Calculate exponential backoff (capped at maxBackoffSeconds)
|
|
1139
|
+
*/
|
|
1140
|
+
calculateBackoff() {
|
|
1141
|
+
if (this.consecutiveFailures === 0) {
|
|
1142
|
+
return 0;
|
|
1143
|
+
}
|
|
1144
|
+
const backoffSeconds = Math.min(
|
|
1145
|
+
Math.pow(2, this.consecutiveFailures) * 1e3,
|
|
1146
|
+
this.maxBackoffSeconds * 1e3
|
|
1147
|
+
);
|
|
1148
|
+
return backoffSeconds;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Stop background heartbeat refresher
|
|
1152
|
+
*/
|
|
1153
|
+
stop() {
|
|
1154
|
+
if (!this.started) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
this.started = false;
|
|
1158
|
+
if (this.refreshTimer) {
|
|
1159
|
+
clearTimeout(this.refreshTimer);
|
|
1160
|
+
this.refreshTimer = null;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Get current heartbeat token if valid
|
|
1165
|
+
*/
|
|
1166
|
+
getToken() {
|
|
1167
|
+
if (!this.currentToken) {
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1171
|
+
if (this.currentToken.expiresAt <= now + 2) {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
return this.currentToken.token;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Check if current heartbeat token is valid
|
|
1178
|
+
*/
|
|
1179
|
+
isValid() {
|
|
1180
|
+
return this.getToken() !== null;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Update signer ID (called when signer is known)
|
|
1184
|
+
*/
|
|
1185
|
+
updateSignerId(signerId) {
|
|
1186
|
+
if (this.signerId !== signerId) {
|
|
1187
|
+
this.signerId = signerId;
|
|
1188
|
+
this.currentToken = null;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Acquire a new heartbeat token from Control Plane
|
|
1193
|
+
* NEVER logs token value (security)
|
|
1194
|
+
*/
|
|
1195
|
+
async acquireHeartbeat() {
|
|
1196
|
+
try {
|
|
1197
|
+
const response = await this.httpClient.request({
|
|
1198
|
+
method: "POST",
|
|
1199
|
+
path: "/api/v1/gate/heartbeat",
|
|
1200
|
+
body: {
|
|
1201
|
+
tenantId: this.tenantId,
|
|
1202
|
+
signerId: this.signerId,
|
|
1203
|
+
environment: this.environment,
|
|
1204
|
+
clientInstanceId: this.clientInstanceId,
|
|
1205
|
+
sdkVersion: this.sdkVersion
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
if (response.success && response.data) {
|
|
1209
|
+
const token = response.data.heartbeatToken;
|
|
1210
|
+
const expiresAt = response.data.expiresAt;
|
|
1211
|
+
if (!token || !expiresAt) {
|
|
1212
|
+
throw new GateError(
|
|
1213
|
+
"INVALID_RESPONSE" /* INVALID_RESPONSE */,
|
|
1214
|
+
"Invalid heartbeat response: missing token or expiresAt"
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
this.currentToken = {
|
|
1218
|
+
token,
|
|
1219
|
+
expiresAt,
|
|
1220
|
+
jti: response.data.jti,
|
|
1221
|
+
policyHash: response.data.policyHash
|
|
1222
|
+
};
|
|
1223
|
+
console.log("[HEARTBEAT] Acquired heartbeat token", {
|
|
1224
|
+
expiresAt,
|
|
1225
|
+
jti: response.data.jti,
|
|
1226
|
+
policyHash: response.data.policyHash?.substring(0, 8) + "..."
|
|
1227
|
+
// DO NOT log token value
|
|
1228
|
+
});
|
|
1229
|
+
} else {
|
|
1230
|
+
const error = response.error || {};
|
|
1231
|
+
throw new GateError(
|
|
1232
|
+
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1233
|
+
`Heartbeat acquisition failed: ${error.message || "Unknown error"}`
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
console.error("[HEARTBEAT] Failed to acquire heartbeat:", error.message || error);
|
|
1238
|
+
throw error;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Get client instance ID (for tracking)
|
|
1243
|
+
*/
|
|
1244
|
+
getClientInstanceId() {
|
|
1245
|
+
return this.clientInstanceId;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
925
1248
|
|
|
926
1249
|
// src/client/GateClient.ts
|
|
927
1250
|
var GateClient = class {
|
|
@@ -932,8 +1255,18 @@ var GateClient = class {
|
|
|
932
1255
|
stepUpPoller;
|
|
933
1256
|
circuitBreaker;
|
|
934
1257
|
metrics;
|
|
1258
|
+
heartbeatManager;
|
|
1259
|
+
mode;
|
|
1260
|
+
onConnectionFailure;
|
|
935
1261
|
constructor(config) {
|
|
936
1262
|
this.config = config;
|
|
1263
|
+
const envMode = process.env.GATE_MODE;
|
|
1264
|
+
this.mode = envMode || config.mode || "SHADOW";
|
|
1265
|
+
if (config.onConnectionFailure) {
|
|
1266
|
+
this.onConnectionFailure = config.onConnectionFailure;
|
|
1267
|
+
} else {
|
|
1268
|
+
this.onConnectionFailure = this.mode === "SHADOW" ? "FAIL_OPEN" : "FAIL_CLOSED";
|
|
1269
|
+
}
|
|
937
1270
|
if (config.auth.mode === "hmac") {
|
|
938
1271
|
this.hmacSigner = new HmacSigner({
|
|
939
1272
|
keyId: config.auth.keyId,
|
|
@@ -964,11 +1297,40 @@ var GateClient = class {
|
|
|
964
1297
|
if (config.onMetrics) {
|
|
965
1298
|
this.metrics.registerHook(config.onMetrics);
|
|
966
1299
|
}
|
|
1300
|
+
if (config.local) {
|
|
1301
|
+
console.warn("[GATE CLIENT] LOCAL MODE ENABLED - Auth, heartbeat, and break-glass are disabled");
|
|
1302
|
+
this.heartbeatManager = null;
|
|
1303
|
+
} else {
|
|
1304
|
+
let controlPlaneUrl = config.baseUrl;
|
|
1305
|
+
if (controlPlaneUrl.includes("/defense")) {
|
|
1306
|
+
controlPlaneUrl = controlPlaneUrl.split("/defense")[0];
|
|
1307
|
+
}
|
|
1308
|
+
if (config.controlPlaneUrl) {
|
|
1309
|
+
controlPlaneUrl = config.controlPlaneUrl;
|
|
1310
|
+
}
|
|
1311
|
+
const heartbeatHttpClient = new HttpClient({
|
|
1312
|
+
baseUrl: controlPlaneUrl,
|
|
1313
|
+
timeoutMs: 5e3,
|
|
1314
|
+
// 5s timeout for heartbeat
|
|
1315
|
+
userAgent: config.userAgent
|
|
1316
|
+
});
|
|
1317
|
+
const initialSignerId = config.signerId ?? "trading-bot-signer";
|
|
1318
|
+
this.heartbeatManager = new HeartbeatManager({
|
|
1319
|
+
httpClient: heartbeatHttpClient,
|
|
1320
|
+
tenantId: config.tenantId,
|
|
1321
|
+
signerId: initialSignerId,
|
|
1322
|
+
environment: config.environment ?? "prod",
|
|
1323
|
+
refreshIntervalSeconds: config.heartbeatRefreshIntervalSeconds ?? 10
|
|
1324
|
+
});
|
|
1325
|
+
this.heartbeatManager.start();
|
|
1326
|
+
}
|
|
967
1327
|
}
|
|
968
1328
|
/**
|
|
969
1329
|
* Evaluate a transaction defense request
|
|
970
1330
|
*
|
|
971
1331
|
* Implements:
|
|
1332
|
+
* - Shadow Mode (SHADOW: monitor-only, ENFORCE: enforce decisions)
|
|
1333
|
+
* - Connection failure strategy (FAIL_OPEN vs FAIL_CLOSED)
|
|
972
1334
|
* - Circuit breaker protection
|
|
973
1335
|
* - Fail-safe modes (ALLOW_ON_TIMEOUT, BLOCK_ON_TIMEOUT, BLOCK_ON_ANOMALY)
|
|
974
1336
|
* - Metrics collection
|
|
@@ -979,7 +1341,29 @@ var GateClient = class {
|
|
|
979
1341
|
const timestampMs = req.timestampMs ?? nowMs();
|
|
980
1342
|
const startTime = Date.now();
|
|
981
1343
|
const failSafeMode = this.config.failSafeMode ?? "ALLOW_ON_TIMEOUT";
|
|
1344
|
+
const requestMode = req.mode || this.mode;
|
|
982
1345
|
const executeRequest = async () => {
|
|
1346
|
+
if (!this.config.local && this.heartbeatManager && req.signingContext?.signerId) {
|
|
1347
|
+
this.heartbeatManager.updateSignerId(req.signingContext.signerId);
|
|
1348
|
+
}
|
|
1349
|
+
let heartbeatToken = null;
|
|
1350
|
+
if (!this.config.local && this.heartbeatManager) {
|
|
1351
|
+
heartbeatToken = this.heartbeatManager.getToken();
|
|
1352
|
+
if (!heartbeatToken) {
|
|
1353
|
+
const maxWaitMs = 2e3;
|
|
1354
|
+
const startTime2 = Date.now();
|
|
1355
|
+
while (!heartbeatToken && Date.now() - startTime2 < maxWaitMs) {
|
|
1356
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1357
|
+
heartbeatToken = this.heartbeatManager.getToken();
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
if (!heartbeatToken) {
|
|
1361
|
+
throw new GateError(
|
|
1362
|
+
"HEARTBEAT_MISSING" /* HEARTBEAT_MISSING */,
|
|
1363
|
+
"Signing blocked: Heartbeat token is missing or expired. Gate must be alive and enforcing policy."
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
983
1367
|
const txIntent = { ...req.txIntent };
|
|
984
1368
|
if (txIntent.to && !txIntent.toAddress) {
|
|
985
1369
|
txIntent.toAddress = txIntent.to;
|
|
@@ -992,9 +1376,11 @@ var GateClient = class {
|
|
|
992
1376
|
delete txIntent.from;
|
|
993
1377
|
}
|
|
994
1378
|
const signingContext = {
|
|
995
|
-
...req.signingContext
|
|
996
|
-
actorPrincipal: req.signingContext?.actorPrincipal || req.signingContext?.signerId || "unknown"
|
|
1379
|
+
...req.signingContext
|
|
997
1380
|
};
|
|
1381
|
+
if (heartbeatToken) {
|
|
1382
|
+
signingContext.heartbeatToken = heartbeatToken;
|
|
1383
|
+
}
|
|
998
1384
|
const provenance = ProvenanceProvider.getProvenance();
|
|
999
1385
|
if (provenance) {
|
|
1000
1386
|
signingContext.caller = {
|
|
@@ -1005,20 +1391,36 @@ var GateClient = class {
|
|
|
1005
1391
|
attestation: provenance.attestation
|
|
1006
1392
|
};
|
|
1007
1393
|
}
|
|
1008
|
-
|
|
1394
|
+
let body = {
|
|
1009
1395
|
requestId,
|
|
1010
|
-
tenantId: this.config.tenantId,
|
|
1011
1396
|
timestampMs,
|
|
1012
1397
|
txIntent,
|
|
1013
1398
|
signingContext,
|
|
1014
1399
|
// Add SDK info (required by Hot Path validation)
|
|
1400
|
+
// Note: Must match Python SDK name for consistent canonical JSON
|
|
1015
1401
|
sdk: {
|
|
1016
|
-
name: "
|
|
1402
|
+
name: "gate-sdk",
|
|
1017
1403
|
version: "0.1.0"
|
|
1018
|
-
}
|
|
1404
|
+
},
|
|
1405
|
+
// Add mode and connection failure strategy
|
|
1406
|
+
mode: requestMode,
|
|
1407
|
+
onConnectionFailure: this.onConnectionFailure
|
|
1019
1408
|
};
|
|
1020
|
-
|
|
1021
|
-
|
|
1409
|
+
if (req.simulate === true) {
|
|
1410
|
+
body.simulate = true;
|
|
1411
|
+
}
|
|
1412
|
+
if (!this.config.local && this.config.breakglassToken) {
|
|
1413
|
+
signingContext.breakglassToken = this.config.breakglassToken;
|
|
1414
|
+
}
|
|
1415
|
+
let headers = {};
|
|
1416
|
+
if (this.config.local) {
|
|
1417
|
+
headers = {
|
|
1418
|
+
"Content-Type": "application/json"
|
|
1419
|
+
};
|
|
1420
|
+
console.log("[GATE CLIENT] LOCAL MODE - Skipping authentication");
|
|
1421
|
+
} else if (this.hmacSigner) {
|
|
1422
|
+
const { canonicalizeJson: canonicalizeJson2 } = await Promise.resolve().then(() => (init_canonicalJson(), canonicalJson_exports));
|
|
1423
|
+
const canonicalBodyJson = canonicalizeJson2(body);
|
|
1022
1424
|
const hmacHeaders = await this.hmacSigner.signRequest({
|
|
1023
1425
|
method: "POST",
|
|
1024
1426
|
path: "/defense/evaluate",
|
|
@@ -1026,8 +1428,19 @@ var GateClient = class {
|
|
|
1026
1428
|
timestampMs,
|
|
1027
1429
|
requestId,
|
|
1028
1430
|
body
|
|
1431
|
+
// Pass original body - HmacSigner will canonicalize it internally
|
|
1029
1432
|
});
|
|
1030
1433
|
headers = { ...hmacHeaders };
|
|
1434
|
+
body.__canonicalJson = canonicalBodyJson;
|
|
1435
|
+
const debugHeaders = {};
|
|
1436
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
1437
|
+
if (key.toLowerCase().includes("signature")) {
|
|
1438
|
+
debugHeaders[key] = value.substring(0, 8) + "...";
|
|
1439
|
+
} else {
|
|
1440
|
+
debugHeaders[key] = value;
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
console.error("[GATE CLIENT DEBUG] HMAC headers prepared:", JSON.stringify(debugHeaders, null, 2));
|
|
1031
1444
|
} else if (this.apiKeyAuth) {
|
|
1032
1445
|
const apiKeyHeaders = this.apiKeyAuth.createHeaders({
|
|
1033
1446
|
tenantId: this.config.tenantId,
|
|
@@ -1035,6 +1448,7 @@ var GateClient = class {
|
|
|
1035
1448
|
requestId
|
|
1036
1449
|
});
|
|
1037
1450
|
headers = { ...apiKeyHeaders };
|
|
1451
|
+
console.error("[GATE CLIENT DEBUG] API key headers prepared:", JSON.stringify(headers, null, 2));
|
|
1038
1452
|
} else {
|
|
1039
1453
|
throw new Error("No authentication configured");
|
|
1040
1454
|
}
|
|
@@ -1045,17 +1459,35 @@ var GateClient = class {
|
|
|
1045
1459
|
body,
|
|
1046
1460
|
requestId
|
|
1047
1461
|
});
|
|
1048
|
-
|
|
1462
|
+
let responseData;
|
|
1463
|
+
if (apiResponse.success === true && apiResponse.data) {
|
|
1464
|
+
responseData = apiResponse.data;
|
|
1465
|
+
} else if (apiResponse.success === false && apiResponse.error) {
|
|
1466
|
+
const error = apiResponse.error;
|
|
1467
|
+
throw new GateError(
|
|
1468
|
+
error.code || "SERVER_ERROR" /* SERVER_ERROR */,
|
|
1469
|
+
error.message || "Request failed",
|
|
1470
|
+
{
|
|
1471
|
+
status: error.status,
|
|
1472
|
+
correlationId: error.correlationId,
|
|
1473
|
+
requestId,
|
|
1474
|
+
details: error
|
|
1475
|
+
}
|
|
1476
|
+
);
|
|
1477
|
+
} else if (apiResponse.decision) {
|
|
1478
|
+
responseData = apiResponse;
|
|
1479
|
+
} else {
|
|
1049
1480
|
throw new GateError(
|
|
1050
1481
|
"INVALID_RESPONSE" /* INVALID_RESPONSE */,
|
|
1051
|
-
"Invalid response format: expected { success: true, data: { ... } }",
|
|
1482
|
+
"Invalid response format: expected { success: true, data: { ... } } or unwrapped response",
|
|
1052
1483
|
{
|
|
1053
1484
|
requestId,
|
|
1054
1485
|
details: apiResponse
|
|
1055
1486
|
}
|
|
1056
1487
|
);
|
|
1057
1488
|
}
|
|
1058
|
-
const
|
|
1489
|
+
const metadata = responseData.metadata || {};
|
|
1490
|
+
const simulationData = metadata.simulation;
|
|
1059
1491
|
const result = {
|
|
1060
1492
|
decision: responseData.decision,
|
|
1061
1493
|
reasonCodes: responseData.reason_codes ?? responseData.reasonCodes ?? [],
|
|
@@ -1064,10 +1496,38 @@ var GateClient = class {
|
|
|
1064
1496
|
stepUp: responseData.step_up ? {
|
|
1065
1497
|
requestId: responseData.step_up.request_id ?? (responseData.stepUp?.requestId ?? ""),
|
|
1066
1498
|
ttlSeconds: responseData.step_up.ttl_seconds ?? responseData.stepUp?.ttlSeconds
|
|
1067
|
-
} : responseData.stepUp
|
|
1499
|
+
} : responseData.stepUp,
|
|
1500
|
+
enforced: responseData.enforced ?? requestMode === "ENFORCE",
|
|
1501
|
+
shadowWouldBlock: responseData.shadow_would_block ?? responseData.shadowWouldBlock ?? false,
|
|
1502
|
+
mode: responseData.mode ?? requestMode,
|
|
1503
|
+
...simulationData ? {
|
|
1504
|
+
simulation: {
|
|
1505
|
+
willRevert: simulationData.willRevert ?? simulationData.will_revert ?? false,
|
|
1506
|
+
gasUsed: simulationData.gasUsed ?? simulationData.gas_used,
|
|
1507
|
+
balanceChanges: simulationData.balanceChanges ?? simulationData.balance_changes,
|
|
1508
|
+
errorReason: simulationData.errorReason ?? simulationData.error_reason
|
|
1509
|
+
},
|
|
1510
|
+
simulationLatencyMs: metadata.simulationLatencyMs ?? metadata.simulation_latency_ms
|
|
1511
|
+
} : {}
|
|
1068
1512
|
};
|
|
1069
1513
|
const latencyMs = Date.now() - startTime;
|
|
1070
1514
|
if (result.decision === "BLOCK") {
|
|
1515
|
+
if (requestMode === "SHADOW") {
|
|
1516
|
+
console.warn("[GATE SHADOW MODE] Would have blocked transaction", {
|
|
1517
|
+
requestId,
|
|
1518
|
+
reasonCodes: result.reasonCodes,
|
|
1519
|
+
correlationId: result.correlationId,
|
|
1520
|
+
tenantId: this.config.tenantId,
|
|
1521
|
+
signerId: req.signingContext?.signerId
|
|
1522
|
+
});
|
|
1523
|
+
this.metrics.recordRequest("WOULD_BLOCK", latencyMs);
|
|
1524
|
+
return {
|
|
1525
|
+
...result,
|
|
1526
|
+
decision: "ALLOW",
|
|
1527
|
+
enforced: false,
|
|
1528
|
+
shadowWouldBlock: true
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1071
1531
|
const receiptId = responseData.decision_id || requestId;
|
|
1072
1532
|
const reasonCode = result.reasonCodes[0] || "POLICY_VIOLATION";
|
|
1073
1533
|
this.metrics.recordRequest("BLOCK", latencyMs);
|
|
@@ -1112,6 +1572,31 @@ var GateClient = class {
|
|
|
1112
1572
|
requestId
|
|
1113
1573
|
);
|
|
1114
1574
|
}
|
|
1575
|
+
const isConnectionFailure = error instanceof GateError && (error.code === "TIMEOUT" /* TIMEOUT */ || error.code === "SERVER_ERROR" /* SERVER_ERROR */) || error instanceof BlockIntelUnavailableError || error?.code === "ECONNREFUSED" || error?.code === "ENOTFOUND" || error?.code === "ETIMEDOUT";
|
|
1576
|
+
if (isConnectionFailure) {
|
|
1577
|
+
this.metrics.recordTimeout();
|
|
1578
|
+
if (this.onConnectionFailure === "FAIL_OPEN") {
|
|
1579
|
+
console.error("[GATE CONNECTION FAILURE] FAIL_OPEN mode - allowing transaction", {
|
|
1580
|
+
requestId,
|
|
1581
|
+
error: error.message,
|
|
1582
|
+
tenantId: this.config.tenantId,
|
|
1583
|
+
mode: requestMode
|
|
1584
|
+
});
|
|
1585
|
+
this.metrics.recordRequest("FAIL_OPEN", Date.now() - startTime);
|
|
1586
|
+
return {
|
|
1587
|
+
decision: "ALLOW",
|
|
1588
|
+
reasonCodes: ["GATE_HOTPATH_UNAVAILABLE"],
|
|
1589
|
+
correlationId: requestId,
|
|
1590
|
+
enforced: false,
|
|
1591
|
+
mode: requestMode
|
|
1592
|
+
};
|
|
1593
|
+
} else {
|
|
1594
|
+
throw new BlockIntelUnavailableError(
|
|
1595
|
+
`Signing blocked: Gate hot path unreachable (fail-closed). ${error.message}`,
|
|
1596
|
+
requestId
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1115
1600
|
if (error instanceof GateError && error.code === "TIMEOUT" /* TIMEOUT */) {
|
|
1116
1601
|
this.metrics.recordTimeout();
|
|
1117
1602
|
const failSafeResult = this.handleFailSafe(failSafeMode, error, requestId);
|
|
@@ -1224,6 +1709,6 @@ function createGateClient(config) {
|
|
|
1224
1709
|
return new GateClient(config);
|
|
1225
1710
|
}
|
|
1226
1711
|
|
|
1227
|
-
export { BlockIntelAuthError, BlockIntelBlockedError, BlockIntelStepUpRequiredError, BlockIntelUnavailableError, GateClient, GateError, GateErrorCode, ProvenanceProvider, StepUpNotConfiguredError, createGateClient, GateClient as default, wrapKmsClient };
|
|
1712
|
+
export { BlockIntelAuthError, BlockIntelBlockedError, BlockIntelStepUpRequiredError, BlockIntelUnavailableError, GateClient, GateError, GateErrorCode, HeartbeatManager, ProvenanceProvider, StepUpNotConfiguredError, createGateClient, GateClient as default, wrapKmsClient };
|
|
1228
1713
|
//# sourceMappingURL=index.js.map
|
|
1229
1714
|
//# sourceMappingURL=index.js.map
|