blockintel-gate-sdk 0.3.5 → 0.3.6

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 CHANGED
@@ -13,12 +13,6 @@ npm install @blockintel/gate-sdk
13
13
  - Node.js >= 18.0.0 (uses global `fetch` API)
14
14
  - TypeScript >= 5.0.0 (optional, for type definitions)
15
15
 
16
- ### Hot Path compatibility
17
-
18
- - **Mode**: Default is `SHADOW` (Hot Path returns ALLOW with reason codes for would-block decisions). Set `mode: 'ENFORCE'` or `GATE_MODE=ENFORCE` for real BLOCK responses.
19
- - **signingContext**: Hot Path requires `actorPrincipal` and `signerId`. The SDK defaults them when missing (`gate-sdk-client` or from `signingContext.signerId`).
20
- - **ESM**: HMAC and SHA-256 use `node:crypto` (no `require('crypto')`), so the SDK works in ESM (`"type": "module"`) and in bundled canary apps.
21
-
22
16
  ## Quick Start
23
17
 
24
18
  ### HMAC Authentication
@@ -209,9 +203,6 @@ When step-up is disabled, the SDK treats `REQUIRE_STEP_UP` as `BLOCK` by default
209
203
  GATE_BASE_URL=https://gate.blockintelai.com
210
204
  GATE_TENANT_ID=your-tenant-id
211
205
 
212
- # Heartbeat (required when not using local mode; parity with Python GATE_HEARTBEAT_KEY)
213
- GATE_HEARTBEAT_KEY=your-heartbeat-key
214
-
215
206
  # HMAC Authentication
216
207
  GATE_KEY_ID=your-key-id
217
208
  GATE_HMAC_SECRET=your-secret
@@ -385,21 +376,6 @@ The SDK automatically retries failed requests:
385
376
  - Same `requestId` is used across all retries
386
377
  - Ensures idempotency on Gate server
387
378
 
388
- ## Degraded Mode / X-BlockIntel-Degraded
389
-
390
- When the SDK is in a degraded situation, it logs `X-BlockIntel-Degraded: true` with a `reason` for **logs and telemetry only**. This is **never sent as an HTTP request header** to the Gate server.
391
-
392
- **Reasons:** `retry`, `429`, `fail_open`, `fail_safe_allow`.
393
-
394
- **Example (one line):**
395
- `[GATE SDK] X-BlockIntel-Degraded: true (reason=retry) attempt=1/3 status=503 err=RATE_LIMITED`
396
-
397
- **How to observe:**
398
- - **Logs:** `[GATE SDK] X-BlockIntel-Degraded: true (reason: <reason>)` via `console.warn`. Pipe stderr to your log aggregator.
399
- - **Metrics:** Use `onMetrics`; metrics include `timeouts`, `errors`, `failOpen`, etc. Correlate with log lines if you ship both.
400
-
401
- **Manual check (retry):** Point the SDK at an endpoint that returns 5xx; confirm one degraded log per retry attempt including `attempt`, `max`, and `status`/`err`.
402
-
403
379
  ## Heartbeat System
404
380
 
405
381
  The SDK includes a **Heartbeat Manager** that automatically acquires and refreshes heartbeat tokens from the Gate Control Plane. Heartbeat tokens are required for all signing operations and ensure that Gate is alive and enforcing policy.
package/dist/index.cjs CHANGED
@@ -2,12 +2,18 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var crypto = require('crypto');
6
5
  var uuid = require('uuid');
7
6
  var clientKms = require('@aws-sdk/client-kms');
7
+ var crypto$1 = require('crypto');
8
8
 
9
9
  var __defProp = Object.defineProperty;
10
10
  var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
12
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
13
+ }) : x)(function(x) {
14
+ if (typeof require !== "undefined") return require.apply(this, arguments);
15
+ throw Error('Dynamic require of "' + x + '" is not supported');
16
+ });
11
17
  var __esm = (fn, res) => function __init() {
12
18
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
19
  };
@@ -44,24 +50,56 @@ function canonicalizeJson(obj) {
44
50
  return JSON.stringify(sorted);
45
51
  }
46
52
  async function sha256Hex(input) {
47
- return crypto.createHash("sha256").update(input, "utf8").digest("hex");
53
+ if (typeof crypto !== "undefined" && crypto.subtle) {
54
+ const encoder = new TextEncoder();
55
+ const data = encoder.encode(input);
56
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
57
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
58
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
59
+ }
60
+ if (typeof __require !== "undefined") {
61
+ const crypto2 = __require("crypto");
62
+ return crypto2.createHash("sha256").update(input, "utf8").digest("hex");
63
+ }
64
+ throw new Error("SHA-256 not available in this environment");
48
65
  }
49
66
  var init_canonicalJson = __esm({
50
67
  "src/utils/canonicalJson.ts"() {
51
68
  }
52
69
  });
70
+
71
+ // src/utils/crypto.ts
53
72
  async function hmacSha256(secret, message) {
54
- const hmac = crypto.createHmac("sha256", secret);
55
- hmac.update(message, "utf8");
56
- const signatureHex = hmac.digest("hex");
57
- console.error("[HMAC CRYPTO DEBUG] Signature computation:", JSON.stringify({
58
- secretLength: secret.length,
59
- messageLength: message.length,
60
- messagePreview: message.substring(0, 200) + "...",
61
- signatureLength: signatureHex.length,
62
- signaturePreview: signatureHex.substring(0, 16) + "..."
63
- }, null, 2));
64
- return signatureHex;
73
+ if (typeof __require !== "undefined") {
74
+ const crypto2 = __require("crypto");
75
+ const hmac = crypto2.createHmac("sha256", secret);
76
+ hmac.update(message, "utf8");
77
+ const signatureHex = hmac.digest("hex");
78
+ console.error("[HMAC CRYPTO DEBUG] Signature computation:", JSON.stringify({
79
+ secretLength: secret.length,
80
+ messageLength: message.length,
81
+ messagePreview: message.substring(0, 200) + "...",
82
+ signatureLength: signatureHex.length,
83
+ signaturePreview: signatureHex.substring(0, 16) + "..."
84
+ }, null, 2));
85
+ return signatureHex;
86
+ }
87
+ if (typeof crypto !== "undefined" && crypto.subtle) {
88
+ const encoder = new TextEncoder();
89
+ const keyData = encoder.encode(secret);
90
+ const messageData = encoder.encode(message);
91
+ const key = await crypto.subtle.importKey(
92
+ "raw",
93
+ keyData,
94
+ { name: "HMAC", hash: "SHA-256" },
95
+ false,
96
+ ["sign"]
97
+ );
98
+ const signature = await crypto.subtle.sign("HMAC", key, messageData);
99
+ const hashArray = Array.from(new Uint8Array(signature));
100
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
101
+ }
102
+ throw new Error("HMAC-SHA256 not available in this environment");
65
103
  }
66
104
 
67
105
  // src/auth/HmacSigner.ts
@@ -94,7 +132,26 @@ var HmacSigner = class {
94
132
  // Used as nonce in canonical string
95
133
  bodyHash
96
134
  ].join("\n");
135
+ console.error("[HMAC SIGNER DEBUG] Canonical request string:", JSON.stringify({
136
+ method: method.toUpperCase(),
137
+ path,
138
+ tenantId,
139
+ keyId: this.keyId,
140
+ timestampMs: String(timestampMs),
141
+ requestId,
142
+ bodyHash,
143
+ signingStringLength: signingString.length,
144
+ signingStringPreview: signingString.substring(0, 200) + "...",
145
+ bodyJsonLength: bodyJson.length,
146
+ bodyJsonPreview: bodyJson.substring(0, 200) + "..."
147
+ }, null, 2));
97
148
  const signature = await hmacSha256(this.secret, signingString);
149
+ console.error("[HMAC SIGNER DEBUG] Signature computed:", JSON.stringify({
150
+ signatureLength: signature.length,
151
+ signaturePreview: signature.substring(0, 16) + "...",
152
+ secretLength: this.secret.length,
153
+ secretPreview: this.secret.substring(0, 4) + "..." + this.secret.substring(this.secret.length - 4)
154
+ }, null, 2));
98
155
  return {
99
156
  "X-GATE-TENANT-ID": tenantId,
100
157
  "X-GATE-KEY-ID": this.keyId,
@@ -293,10 +350,6 @@ async function retryWithBackoff(fn, options = {}) {
293
350
  if (!isRetryable) {
294
351
  throw error;
295
352
  }
296
- const status = error instanceof Response && error.status || error && typeof error === "object" && "status" in error && error.status || error && typeof error === "object" && "statusCode" in error && error.statusCode;
297
- const errName = error instanceof Error ? error.name : error && typeof error === "object" && "code" in error ? error.code : "Unknown";
298
- const extra = ` attempt=${attempt}/${opts.maxAttempts} status=${status ?? "n/a"} err=${errName}`;
299
- console.warn("[GATE SDK] X-BlockIntel-Degraded: true (reason=retry)" + extra);
300
353
  const delay = calculateBackoffDelay(attempt, opts);
301
354
  await new Promise((resolve) => setTimeout(resolve, delay));
302
355
  }
@@ -304,73 +357,17 @@ async function retryWithBackoff(fn, options = {}) {
304
357
  throw lastError;
305
358
  }
306
359
 
307
- // src/utils/sanitize.ts
308
- var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
309
- "authorization",
310
- "x-api-key",
311
- "x-gate-heartbeat-key",
312
- "x-gate-signature",
313
- "cookie"
314
- ]);
315
- var MAX_STRING_LENGTH = 80;
316
- function sanitizeHeaders(headers) {
317
- const out = {};
318
- for (const [key, value] of Object.entries(headers)) {
319
- const lower = key.toLowerCase();
320
- if (SENSITIVE_HEADER_NAMES.has(lower) || lower.includes("signature") || lower.includes("secret") || lower.includes("token")) {
321
- out[key] = value ? "[REDACTED]" : "[empty]";
322
- } else {
323
- out[key] = truncate(String(value), MAX_STRING_LENGTH);
324
- }
325
- }
326
- return out;
327
- }
328
- function sanitizeBodyShape(body) {
329
- if (body === null || body === void 0) {
330
- return {};
331
- }
332
- if (typeof body !== "object") {
333
- return { _: typeof body };
334
- }
335
- if (Array.isArray(body)) {
336
- return { _: "array", length: String(body.length) };
337
- }
338
- const out = {};
339
- for (const key of Object.keys(body).sort()) {
340
- const val = body[key];
341
- if (val !== null && typeof val === "object" && !Array.isArray(val)) {
342
- out[key] = "object";
343
- } else if (Array.isArray(val)) {
344
- out[key] = "array";
345
- } else {
346
- out[key] = typeof val;
347
- }
348
- }
349
- return out;
350
- }
351
- function truncate(s, max) {
352
- if (s.length <= max) return s;
353
- return s.slice(0, max) + "...";
354
- }
355
- function isDebugEnabled(debugOption) {
356
- if (debugOption === true) return true;
357
- if (typeof process !== "undefined" && process.env.GATE_SDK_DEBUG === "1") return true;
358
- return false;
359
- }
360
-
361
360
  // src/http/HttpClient.ts
362
361
  var HttpClient = class {
363
362
  baseUrl;
364
363
  timeoutMs;
365
364
  userAgent;
366
365
  retryOptions;
367
- debug;
368
366
  constructor(config) {
369
367
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
370
368
  this.timeoutMs = config.timeoutMs ?? 15e3;
371
369
  this.userAgent = config.userAgent ?? "blockintel-gate-sdk/0.1.0";
372
370
  this.retryOptions = config.retryOptions;
373
- this.debug = isDebugEnabled(config.debug);
374
371
  if (!this.baseUrl) {
375
372
  throw new Error("baseUrl is required");
376
373
  }
@@ -389,6 +386,7 @@ var HttpClient = class {
389
386
  const controller = new AbortController();
390
387
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
391
388
  let requestDetailsForLogging = null;
389
+ let requestDetailsSet = false;
392
390
  try {
393
391
  const response = await retryWithBackoff(
394
392
  async () => {
@@ -411,22 +409,31 @@ var HttpClient = class {
411
409
  fetchOptions.body = JSON.stringify(body);
412
410
  }
413
411
  }
412
+ const logHeaders = {};
413
+ if (fetchOptions.headers) {
414
+ Object.entries(fetchOptions.headers).forEach(([key, value]) => {
415
+ if (key.toLowerCase().includes("signature") || key.toLowerCase().includes("secret")) {
416
+ logHeaders[key] = String(value).substring(0, 8) + "...";
417
+ } else {
418
+ logHeaders[key] = String(value);
419
+ }
420
+ });
421
+ }
414
422
  const bodyStr = typeof fetchOptions.body === "string" ? fetchOptions.body : null;
415
- requestDetailsForLogging = {
416
- headers: this.debug ? sanitizeHeaders(requestHeaders) : {},
417
- bodyLength: bodyStr ? bodyStr.length : 0
423
+ const details = {
424
+ headers: logHeaders,
425
+ bodyLength: bodyStr ? bodyStr.length : 0,
426
+ bodyPreview: bodyStr ? bodyStr.substring(0, 300) : null
418
427
  };
419
- if (this.debug) {
420
- const bodyShape = body && typeof body === "object" ? sanitizeBodyShape(body) : {};
421
- console.error("[GATE SDK] Request:", JSON.stringify({
422
- url,
423
- method,
424
- headerNames: Object.keys(requestHeaders),
425
- headersRedacted: requestDetailsForLogging.headers,
426
- bodyLength: requestDetailsForLogging.bodyLength,
427
- bodyKeysAndTypes: bodyShape
428
- }, null, 2));
429
- }
428
+ requestDetailsForLogging = details;
429
+ requestDetailsSet = true;
430
+ console.error("[HTTP CLIENT DEBUG] Sending request:", JSON.stringify({
431
+ url,
432
+ method,
433
+ headers: logHeaders,
434
+ bodyLength: requestDetailsForLogging.bodyLength,
435
+ bodyPreview: requestDetailsForLogging.bodyPreview
436
+ }, null, 2));
430
437
  const res = await fetch(url, fetchOptions);
431
438
  if (!res.ok && isRetryableStatus(res.status)) {
432
439
  throw res;
@@ -444,24 +451,26 @@ var HttpClient = class {
444
451
  clearTimeout(timeoutId);
445
452
  let data;
446
453
  const contentType = response.headers.get("content-type");
447
- if (this.debug) {
448
- console.error("[GATE SDK] Response:", JSON.stringify({
449
- status: response.status,
450
- ok: response.ok,
451
- url: response.url
452
- }, null, 2));
453
- }
454
+ console.error("[HTTP CLIENT DEBUG] Response received:", JSON.stringify({
455
+ status: response.status,
456
+ ok: response.ok,
457
+ statusText: response.statusText,
458
+ contentType,
459
+ url: response.url
460
+ }, null, 2));
454
461
  if (contentType && contentType.includes("application/json")) {
455
462
  try {
456
463
  const jsonText = await response.text();
464
+ console.error("[HTTP CLIENT DEBUG] Response body (first 500 chars):", jsonText.substring(0, 500));
457
465
  data = JSON.parse(jsonText);
458
- if (this.debug && data && typeof data === "object") {
459
- console.error("[GATE SDK] Response keys:", Object.keys(data));
460
- }
466
+ console.error("[HTTP CLIENT DEBUG] Parsed JSON:", JSON.stringify({
467
+ hasSuccess: typeof data?.success !== "undefined",
468
+ success: data?.success,
469
+ hasData: typeof data?.data !== "undefined",
470
+ hasError: typeof data?.error !== "undefined"
471
+ }, null, 2));
461
472
  } catch (parseError) {
462
- if (this.debug) {
463
- console.error("[GATE SDK] JSON parse error:", parseError instanceof Error ? parseError.message : String(parseError));
464
- }
473
+ console.error("[HTTP CLIENT DEBUG] JSON parse error:", parseError);
465
474
  throw new GateError(
466
475
  "INVALID_RESPONSE" /* INVALID_RESPONSE */,
467
476
  "Failed to parse JSON response",
@@ -489,12 +498,26 @@ var HttpClient = class {
489
498
  response.headers.forEach((value, key) => {
490
499
  responseHeaders[key] = value;
491
500
  });
492
- if (this.debug) {
493
- console.error("[GATE SDK] Error response:", JSON.stringify({
501
+ if (response.status === 401) {
502
+ console.error("[HTTP CLIENT DEBUG] 401 UNAUTHORIZED - Full request details:", JSON.stringify({
494
503
  status: response.status,
504
+ statusText: response.statusText,
495
505
  url: response.url,
506
+ requestMethod: method,
496
507
  requestPath: path,
497
- responseKeys: data && typeof data === "object" ? Object.keys(data) : []
508
+ requestHeaders: requestDetailsForLogging ? requestDetailsForLogging.headers : {},
509
+ responseHeaders,
510
+ responseData: data,
511
+ bodyLength: requestDetailsForLogging ? requestDetailsForLogging.bodyLength : 0,
512
+ bodyPreview: requestDetailsForLogging ? requestDetailsForLogging.bodyPreview : null
513
+ }, null, 2));
514
+ } else {
515
+ console.error("[HTTP CLIENT DEBUG] Response not OK:", JSON.stringify({
516
+ status: response.status,
517
+ statusText: response.statusText,
518
+ url: response.url,
519
+ headers: responseHeaders,
520
+ data
498
521
  }, null, 2));
499
522
  }
500
523
  const errorCode = this.statusToErrorCode(response.status);
@@ -506,6 +529,7 @@ var HttpClient = class {
506
529
  details: data
507
530
  });
508
531
  }
532
+ console.error("[HTTP CLIENT DEBUG] Response OK, returning data");
509
533
  return data;
510
534
  } catch (error) {
511
535
  clearTimeout(timeoutId);
@@ -958,7 +982,7 @@ function defaultExtractTxIntent(command) {
958
982
  throw new Error("SignCommand missing required Message property");
959
983
  }
960
984
  const messageBuffer = message instanceof Buffer ? message : Buffer.from(message);
961
- const messageHash = crypto.createHash("sha256").update(messageBuffer).digest("hex");
985
+ const messageHash = crypto$1.createHash("sha256").update(messageBuffer).digest("hex");
962
986
  return {
963
987
  networkFamily: "OTHER",
964
988
  toAddress: void 0,
@@ -1064,8 +1088,6 @@ var HeartbeatManager = class {
1064
1088
  // Unique per process
1065
1089
  sdkVersion;
1066
1090
  // SDK version for tracking
1067
- apiKey;
1068
- // x-gate-heartbeat-key for Control Plane auth
1069
1091
  currentToken = null;
1070
1092
  refreshTimer = null;
1071
1093
  started = false;
@@ -1078,22 +1100,19 @@ var HeartbeatManager = class {
1078
1100
  this.signerId = options.signerId;
1079
1101
  this.environment = options.environment ?? "prod";
1080
1102
  this.baseRefreshIntervalSeconds = options.refreshIntervalSeconds ?? 10;
1081
- this.apiKey = options.apiKey;
1082
1103
  this.clientInstanceId = options.clientInstanceId || uuid.v4();
1083
1104
  this.sdkVersion = options.sdkVersion || "1.0.0";
1084
- this.apiKey = options.apiKey;
1085
1105
  }
1086
1106
  /**
1087
- * Start background heartbeat refresher.
1088
- * Optionally wait for initial token (first evaluate() will otherwise wait up to 2s for token).
1107
+ * Start background heartbeat refresher
1089
1108
  */
1090
- start(options) {
1109
+ start() {
1091
1110
  if (this.started) {
1092
1111
  return;
1093
1112
  }
1094
1113
  this.started = true;
1095
1114
  this.acquireHeartbeat().catch((error) => {
1096
- console.error("[HEARTBEAT] Failed to acquire initial heartbeat:", error instanceof Error ? error.message : error);
1115
+ console.error("[HEARTBEAT] Failed to acquire initial heartbeat:", error);
1097
1116
  });
1098
1117
  this.scheduleNextRefresh();
1099
1118
  }
@@ -1176,23 +1195,12 @@ var HeartbeatManager = class {
1176
1195
  /**
1177
1196
  * Acquire a new heartbeat token from Control Plane
1178
1197
  * NEVER logs token value (security)
1179
- * Requires x-gate-heartbeat-key header (apiKey) for authentication.
1180
1198
  */
1181
1199
  async acquireHeartbeat() {
1182
- if (!this.apiKey || this.apiKey.length === 0) {
1183
- throw new GateError(
1184
- "UNAUTHORIZED" /* UNAUTHORIZED */,
1185
- "Heartbeat API key is required. Set GATE_HEARTBEAT_KEY in environment or pass heartbeatApiKey in GateClientConfig.",
1186
- {}
1187
- );
1188
- }
1189
1200
  try {
1190
1201
  const response = await this.httpClient.request({
1191
1202
  method: "POST",
1192
1203
  path: "/api/v1/gate/heartbeat",
1193
- headers: {
1194
- "x-gate-heartbeat-key": this.apiKey
1195
- },
1196
1204
  body: {
1197
1205
  tenantId: this.tenantId,
1198
1206
  signerId: this.signerId,
@@ -1520,8 +1528,7 @@ var GateClient = class {
1520
1528
  this.httpClient = new HttpClient({
1521
1529
  baseUrl: config.baseUrl,
1522
1530
  timeoutMs: config.timeoutMs,
1523
- userAgent: config.userAgent,
1524
- debug: config.debug
1531
+ userAgent: config.userAgent
1525
1532
  });
1526
1533
  if (config.enableStepUp) {
1527
1534
  this.stepUpPoller = new StepUpPoller({
@@ -1542,12 +1549,6 @@ var GateClient = class {
1542
1549
  console.warn("[GATE CLIENT] LOCAL MODE ENABLED - Auth, heartbeat, and break-glass are disabled");
1543
1550
  this.heartbeatManager = null;
1544
1551
  } else {
1545
- const heartbeatApiKey = config.heartbeatApiKey ?? (typeof process !== "undefined" ? process.env.GATE_HEARTBEAT_KEY : void 0);
1546
- if (!heartbeatApiKey || heartbeatApiKey.length === 0) {
1547
- throw new Error(
1548
- "GATE_HEARTBEAT_KEY environment variable or heartbeatApiKey in config is required for heartbeat authentication. Set GATE_HEARTBEAT_KEY in your environment or pass heartbeatApiKey in GateClientConfig."
1549
- );
1550
- }
1551
1552
  let controlPlaneUrl = config.baseUrl;
1552
1553
  if (controlPlaneUrl.includes("/defense")) {
1553
1554
  controlPlaneUrl = controlPlaneUrl.split("/defense")[0];
@@ -1567,8 +1568,7 @@ var GateClient = class {
1567
1568
  tenantId: config.tenantId,
1568
1569
  signerId: initialSignerId,
1569
1570
  environment: config.environment ?? "prod",
1570
- refreshIntervalSeconds: config.heartbeatRefreshIntervalSeconds ?? 10,
1571
- apiKey: heartbeatApiKey
1571
+ refreshIntervalSeconds: config.heartbeatRefreshIntervalSeconds ?? 10
1572
1572
  });
1573
1573
  this.heartbeatManager.start();
1574
1574
  }
@@ -1657,9 +1657,7 @@ var GateClient = class {
1657
1657
  delete txIntent.from;
1658
1658
  }
1659
1659
  const signingContext = {
1660
- ...req.signingContext,
1661
- actorPrincipal: req.signingContext?.actorPrincipal ?? req.signingContext?.signerId ?? "gate-sdk-client",
1662
- signerId: req.signingContext?.signerId ?? req.signingContext?.actorPrincipal ?? "gate-sdk-client"
1660
+ ...req.signingContext
1663
1661
  };
1664
1662
  if (heartbeatToken) {
1665
1663
  signingContext.heartbeatToken = heartbeatToken;
@@ -1675,16 +1673,17 @@ var GateClient = class {
1675
1673
  };
1676
1674
  }
1677
1675
  let body = {
1678
- tenantId: this.config.tenantId,
1679
1676
  requestId,
1680
1677
  timestampMs,
1681
1678
  txIntent,
1682
1679
  signingContext,
1683
1680
  // Add SDK info (required by Hot Path validation)
1681
+ // Note: Must match Python SDK name for consistent canonical JSON
1684
1682
  sdk: {
1685
1683
  name: "gate-sdk",
1686
1684
  version: "0.1.0"
1687
1685
  },
1686
+ // Add mode and connection failure strategy
1688
1687
  mode: requestMode,
1689
1688
  onConnectionFailure: this.onConnectionFailure
1690
1689
  };
@@ -1714,6 +1713,15 @@ var GateClient = class {
1714
1713
  });
1715
1714
  headers = { ...hmacHeaders };
1716
1715
  body.__canonicalJson = canonicalBodyJson;
1716
+ const debugHeaders = {};
1717
+ Object.entries(headers).forEach(([key, value]) => {
1718
+ if (key.toLowerCase().includes("signature")) {
1719
+ debugHeaders[key] = value.substring(0, 8) + "...";
1720
+ } else {
1721
+ debugHeaders[key] = value;
1722
+ }
1723
+ });
1724
+ console.error("[GATE CLIENT DEBUG] HMAC headers prepared:", JSON.stringify(debugHeaders, null, 2));
1717
1725
  } else if (this.apiKeyAuth) {
1718
1726
  const apiKeyHeaders = this.apiKeyAuth.createHeaders({
1719
1727
  tenantId: this.config.tenantId,
@@ -1721,6 +1729,7 @@ var GateClient = class {
1721
1729
  requestId
1722
1730
  });
1723
1731
  headers = { ...apiKeyHeaders };
1732
+ console.error("[GATE CLIENT DEBUG] API key headers prepared:", JSON.stringify(headers, null, 2));
1724
1733
  } else {
1725
1734
  throw new Error("No authentication configured");
1726
1735
  }
@@ -1854,7 +1863,6 @@ var GateClient = class {
1854
1863
  tenantId: this.config.tenantId,
1855
1864
  mode: requestMode
1856
1865
  });
1857
- console.warn("[GATE SDK] X-BlockIntel-Degraded: true (reason: fail_open)");
1858
1866
  this.metrics.recordRequest("FAIL_OPEN", Date.now() - startTime);
1859
1867
  return {
1860
1868
  decision: "ALLOW",
@@ -1886,10 +1894,6 @@ var GateClient = class {
1886
1894
  }
1887
1895
  throw error;
1888
1896
  }
1889
- if (error instanceof GateError && error.code === "RATE_LIMITED" /* RATE_LIMITED */) {
1890
- console.warn("[GATE SDK] X-BlockIntel-Degraded: true (reason: 429)");
1891
- throw error;
1892
- }
1893
1897
  if (error instanceof BlockIntelBlockedError || error instanceof BlockIntelStepUpRequiredError) {
1894
1898
  throw error;
1895
1899
  }
@@ -1902,7 +1906,6 @@ var GateClient = class {
1902
1906
  */
1903
1907
  handleFailSafe(mode, error, requestId) {
1904
1908
  if (mode === "ALLOW_ON_TIMEOUT") {
1905
- console.warn("[GATE SDK] X-BlockIntel-Degraded: true (reason: fail_safe_allow)");
1906
1909
  return {
1907
1910
  decision: "ALLOW",
1908
1911
  reasonCodes: ["FAIL_SAFE_ALLOW"],
@@ -1913,7 +1916,6 @@ var GateClient = class {
1913
1916
  return null;
1914
1917
  }
1915
1918
  if (mode === "BLOCK_ON_ANOMALY") {
1916
- console.warn("[GATE SDK] X-BlockIntel-Degraded: true (reason: fail_safe_allow)");
1917
1919
  return {
1918
1920
  decision: "ALLOW",
1919
1921
  reasonCodes: ["FAIL_SAFE_ALLOW"],
@@ -1988,10 +1990,26 @@ function createGateClient(config) {
1988
1990
  return new GateClient(config);
1989
1991
  }
1990
1992
 
1993
+ // src/client/Gate.ts
1994
+ var Gate = class {
1995
+ apiKey;
1996
+ constructor(opts) {
1997
+ this.apiKey = opts?.apiKey ?? process.env.BLOCKINTEL_API_KEY;
1998
+ }
1999
+ /**
2000
+ * Guard a signing operation. In passthrough mode, executes the callback.
2001
+ * For full Gate integration, use GateClient with evaluate() before sending.
2002
+ */
2003
+ async guard(_ctx, cb) {
2004
+ return cb();
2005
+ }
2006
+ };
2007
+
1991
2008
  exports.BlockIntelAuthError = BlockIntelAuthError;
1992
2009
  exports.BlockIntelBlockedError = BlockIntelBlockedError;
1993
2010
  exports.BlockIntelStepUpRequiredError = BlockIntelStepUpRequiredError;
1994
2011
  exports.BlockIntelUnavailableError = BlockIntelUnavailableError;
2012
+ exports.Gate = Gate;
1995
2013
  exports.GateClient = GateClient;
1996
2014
  exports.GateError = GateError;
1997
2015
  exports.GateErrorCode = GateErrorCode;