npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

blockintel-gate-sdk

v0.3.5

Published

Production-grade TypeScript/Node.js SDK for BlockIntel Gate Hot Path

Readme

BlockIntel Gate SDK

Production-grade TypeScript/Node.js SDK for BlockIntel Gate Hot Path API.

Installation

npm install @blockintel/gate-sdk

Requirements

  • Node.js >= 18.0.0 (uses global fetch API)
  • TypeScript >= 5.0.0 (optional, for type definitions)

Hot Path compatibility

  • 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.
  • signingContext: Hot Path requires actorPrincipal and signerId. The SDK defaults them when missing (gate-sdk-client or from signingContext.signerId).
  • ESM: HMAC and SHA-256 use node:crypto (no require('crypto')), so the SDK works in ESM ("type": "module") and in bundled canary apps.

Quick Start

HMAC Authentication

import { GateClient } from '@blockintel/gate-sdk';

const gate = new GateClient({
  baseUrl: process.env.GATE_BASE_URL!,
  tenantId: process.env.GATE_TENANT_ID!,
  auth: {
    mode: 'hmac',
    keyId: process.env.GATE_KEY_ID!,
    secret: process.env.GATE_HMAC_SECRET!,
  },
  enableStepUp: true,
});

// Evaluate a transaction
const response = await gate.evaluate({
  txIntent: {
    from: '0x1234567890123456789012345678901234567890',
    to: '0x0987654321098765432109876543210987654321',
    value: '1000000000000000000', // 1 ETH in wei
    data: '0x...',
    nonce: 42,
    gasPrice: '20000000000',
    gasLimit: '21000',
    chainId: 1,
  },
  signingContext: {
    signerId: 'my-signer-id',
    source: {
      repo: 'myorg/myrepo',
      workflow: 'deploy-production',
      environment: 'production',
    },
    wallet: {
      address: '0x1234...',
      type: 'hardware',
    },
  },
});

if (response.decision === 'ALLOW') {
  // Proceed with transaction
  console.log('Transaction approved:', response.correlationId);
} else if (response.decision === 'REQUIRE_STEP_UP') {
  // Poll for step-up decision
  const final = await gate.awaitStepUpDecision({
    requestId: response.stepUp!.requestId,
  });

  if (final.status === 'APPROVED') {
    // Proceed with transaction
    console.log('Step-up approved:', final.correlationId);
  } else {
    // Block transaction
    console.log('Step-up denied or expired:', final.status);
  }
} else {
  // BLOCK
  console.log('Transaction blocked:', response.reasonCodes);
}

API Key Authentication

import { GateClient } from '@blockintel/gate-sdk';

const gate = new GateClient({
  baseUrl: process.env.GATE_BASE_URL!,
  tenantId: process.env.GATE_TENANT_ID!,
  auth: {
    mode: 'apiKey',
    apiKey: process.env.GATE_API_KEY!,
  },
});

const response = await gate.evaluate({
  txIntent: {
    from: '0x123...',
    to: '0x456...',
    value: '1000000000000000000',
  },
});

Local Development with Gate Local

For local development and testing, use Gate Local - a Docker container that emulates the Gate Hot Path:

# Start Gate Local
docker pull blockintelai/gate-local:latest
docker run -d --name gate-local -p 3000:3000 blockintelai/gate-local:latest

Then configure your client for local mode:

import { GateClient } from '@blockintel/gate-sdk';

// Local development configuration
const gate = new GateClient({
  baseUrl: 'http://localhost:3000',  // Gate Local endpoint
  tenantId: 'local-dev',              // Any tenant ID (ignored in local mode)
  local: true,                        // Enable local mode (disables auth/heartbeat)
  auth: {
    mode: 'apiKey',
    apiKey: 'local-dev-key'           // Any API key (ignored in local mode)
  },
});

// Evaluate transactions locally
const response = await gate.evaluate({
  txIntent: {
    toAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
    value: '1000000000000000000',  // 1 ETH in wei
    valueUsd: 2500.0,
    chainId: 1,
  },
});

console.log(`Decision: ${response.decision}`);

📚 Full Local Development Guide: See Gate Local Quick Start Guide for complete setup instructions, trading bot integration examples, and troubleshooting.

Step-Up Polling

// Manual polling
const status = await gate.getStepUpStatus({
  requestId: 'stepup-request-id',
});

console.log('Status:', status.status); // PENDING | APPROVED | DENIED | EXPIRED

// Automatic polling with timeout
const result = await gate.awaitStepUpDecision({
  requestId: 'stepup-request-id',
  maxWaitMs: 15000, // 15 seconds
  intervalMs: 250,  // Poll every 250ms
});

console.log('Final status:', result.status);
console.log('Elapsed time:', result.elapsedMs, 'ms');

Polling behavior:

  • 404 NOT_FOUND → request ID does not exist OR does not belong to the tenant
  • EXPIRED → TTL exceeded (deterministic), even if DynamoDB TTL has not deleted the item yet
  • PENDING → waiting for external approval
  • APPROVED | DENIED → terminal states

Configuration

GateClientConfig

interface GateClientConfig {
  baseUrl: string;                    // Gate Hot Path API base URL
  tenantId: string;                   // Your tenant ID
  auth:                                // Authentication
    | { mode: 'hmac'; keyId: string; secret: string }
    | { mode: 'apiKey'; apiKey: string };
  timeoutMs?: number;                 // Request timeout (default: 15000ms)
  userAgent?: string;                 // User agent (default: '@blockintel/gate-sdk/<version>')
  clockSkewMs?: number;               // Clock skew tolerance (default: 120000ms)
  enableStepUp?: boolean;             // Enable step-up support (default: false)
  stepUp?: {
    pollingIntervalMs?: number;       // Polling interval (default: 250ms)
    maxWaitMs?: number;               // Max wait time (default: 15000ms)
    treatRequireStepUpAsBlockWhenDisabled?: boolean; // Transform REQUIRE_STEP_UP to BLOCK (default: true)
  };

When step-up is disabled, the SDK treats REQUIRE_STEP_UP as BLOCK by default to preserve Gate-only safety, unless the caller explicitly overrides this behavior. }


### Environment Variables

```bash
# Required
GATE_BASE_URL=https://gate.blockintelai.com
GATE_TENANT_ID=your-tenant-id

# Heartbeat (required when not using local mode; parity with Python GATE_HEARTBEAT_KEY)
GATE_HEARTBEAT_KEY=your-heartbeat-key

# HMAC Authentication
GATE_KEY_ID=your-key-id
GATE_HMAC_SECRET=your-secret

# OR API Key Authentication
GATE_API_KEY=your-api-key

API Reference

GateClient.evaluate(req, opts?)

Evaluate a transaction defense request.

Parameters:

  • req: DefenseEvaluateRequestV2 - Transaction and signing context
  • opts?: { requestId?: string } - Optional request ID (auto-generated if not provided)

Returns: Promise<DefenseEvaluateResponseV2>

Response:

{
  decision: 'ALLOW' | 'BLOCK' | 'REQUIRE_STEP_UP';
  reasonCodes: string[];
  policyVersion?: string;
  correlationId?: string;
  stepUp?: {
    requestId: string;
    ttlSeconds?: number;
  };
}

GateClient.getStepUpStatus(args)

Get current step-up status.

Parameters:

  • args: { requestId: string; tenantId?: string }

Returns: Promise<StepUpStatusResponse>

Status Types:

  • PENDING - Waiting for decision
  • APPROVED - Step-up approved
  • DENIED - Step-up denied
  • EXPIRED - Step-up expired (TTL exceeded)

Polling behavior:

  • Returns 404 NOT_FOUND if request ID does not exist OR does not belong to the tenant
  • Returns EXPIRED deterministically if TTL exceeded, even if DynamoDB TTL has not deleted the item yet

GateClient.awaitStepUpDecision(args)

Poll step-up status until decision is reached or timeout.

Parameters:

  • args: { requestId: string; maxWaitMs?: number; intervalMs?: number }

Returns: Promise<StepUpFinalResult>

Error Handling

The SDK uses custom error types:

import { GateError, GateErrorCode, StepUpNotConfiguredError } from '@blockintel/gate-sdk';

try {
  const response = await gate.evaluate({ ... });
} catch (error) {
  if (error instanceof GateError) {
    console.error('Error code:', error.code);
    console.error('Status:', error.status);
    console.error('Request ID:', error.requestId);
    console.error('Correlation ID:', error.correlationId);
  }
}

Error Codes:

  • NETWORK_ERROR - Network connection failed
  • TIMEOUT - Request timeout
  • NOT_FOUND - Resource not found (404)
  • UNAUTHORIZED - Authentication failed (401)
  • FORBIDDEN - Access denied (403)
  • RATE_LIMITED - Rate limit exceeded (429)
  • SERVER_ERROR - Server error (5xx)
  • INVALID_RESPONSE - Invalid response format
  • STEP_UP_NOT_CONFIGURED - Step-up required but not enabled
  • STEP_UP_TIMEOUT - Step-up polling timeout
  • HEARTBEAT_MISSING - Heartbeat token is missing or expired
  • HEARTBEAT_EXPIRED - Heartbeat token has expired
  • HEARTBEAT_INVALID - Heartbeat token is invalid
  • HEARTBEAT_MISMATCH - Heartbeat token does not match expected parameters

Authentication

HMAC v1 Signing

The SDK implements HMAC v1 signing for secure authentication:

Signing String:

v1\n
<HTTP_METHOD>\n
<PATH>\n
<TENANT_ID>\n
<KEY_ID>\n
<TIMESTAMP_MS>\n
<REQUEST_ID>\n
<SHA256_HEX_OF_BODY>\n

Signature:

HMAC-SHA256(secret, signingString) as hex

Headers:

  • X-GATE-TENANT-ID
  • X-GATE-KEY-ID
  • X-GATE-TIMESTAMP-MS
  • X-GATE-REQUEST-ID
  • X-GATE-SIGNATURE

API Key

For simpler onboarding, use API key authentication:

Headers:

  • X-API-KEY
  • X-GATE-TENANT-ID
  • X-GATE-REQUEST-ID
  • X-GATE-TIMESTAMP-MS

Step-Up Flow

Step-up is a feature-flagged capability that allows Gate to defer decisions to an external approval system.

Flow:

  1. SDK calls evaluate() → Gate returns REQUIRE_STEP_UP
  2. SDK polls /defense/stepup/status until decision is reached
  3. External system (Control Plane) approves/denies via separate API
  4. SDK receives final decision: APPROVED, DENIED, or EXPIRED

Important:

  • Hot Path never approves/denies step-up
  • Approve/deny happens only on Control Plane
  • SDK only polls status from Hot Path
  • The SDK never performs approve/deny actions. Step-up resolution is handled exclusively by the Control Plane.

Gate-only deployments should leave step-up disabled; the SDK will never "wait" unless step-up is enabled.

TTL Guardrails:

  • Default: 600 seconds
  • Min: 300 seconds
  • Max: 900 seconds

Retry Logic

The SDK automatically retries failed requests:

  • Max Attempts: 3
  • Retry On: Network errors, timeouts, 429, 5xx
  • Never Retry On: 4xx (except 429)
  • Backoff: Exponential with jitter (100ms base, 2x factor, 800ms max)

Request ID Stability:

  • Same requestId is used across all retries
  • Ensures idempotency on Gate server

Degraded Mode / X-BlockIntel-Degraded

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.

Reasons: retry, 429, fail_open, fail_safe_allow.

Example (one line):
[GATE SDK] X-BlockIntel-Degraded: true (reason=retry) attempt=1/3 status=503 err=RATE_LIMITED

How to observe:

  • Logs: [GATE SDK] X-BlockIntel-Degraded: true (reason: <reason>) via console.warn. Pipe stderr to your log aggregator.
  • Metrics: Use onMetrics; metrics include timeouts, errors, failOpen, etc. Correlate with log lines if you ship both.

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.

Heartbeat System

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.

How It Works

  1. Automatic Token Acquisition: The SDK automatically starts a background heartbeat refresher when the GateClient is initialized. This continuously sends heartbeats to the Control Plane, keeping the signer status active in the UI.
  2. Token Refresh: Heartbeat tokens are refreshed every 10 seconds (configurable via heartbeatRefreshIntervalSeconds) to maintain a valid token
  3. Signing Enforcement: Before any evaluate() call, the SDK checks for a valid heartbeat token. If missing or expired, it throws HEARTBEAT_MISSING error
  4. Token Inclusion: The heartbeat token is automatically included in the signingContext of every evaluation request
  5. No Manual Scripts Needed: The SDK handles all heartbeat management automatically - no need for separate heartbeat scripts

Configuration

The heartbeat manager is automatically configured based on your GateClientConfig:

const gate = new GateClient({
  baseUrl: 'https://gate.blockintelai.com',  // Hot Path URL
  tenantId: 'your-tenant-id',
  auth: { mode: 'hmac', ... },
  // Heartbeat manager uses baseUrl to infer Control Plane URL
  // Or explicitly set controlPlaneUrl if different
  controlPlaneUrl: 'https://control-plane.blockintelai.com',  // Optional
  signerId: 'my-signer-id',  // Optional: signerId for heartbeat (if known upfront)
  heartbeatRefreshIntervalSeconds: 10,  // Optional: heartbeat refresh interval (default: 10s)
});

Heartbeat Token Properties

  • TTL: 15-30 seconds (short-lived for security)
  • Scope: Scoped to tenantId, signerId, environment, and policyVersion
  • Validation: Hot Path validates heartbeat tokens before processing any transaction
  • Enforcement: "No valid heartbeat → NO SIGNATURE" - transactions are blocked if heartbeat is missing or expired

Error Handling

import { GateError, GateErrorCode } from '@blockintel/gate-sdk';

try {
  const response = await gate.evaluate({ ... });
} catch (error) {
  if (error instanceof GateError) {
    if (error.code === GateErrorCode.HEARTBEAT_MISSING) {
      console.error('Heartbeat token missing - Gate may be down or unreachable');
    } else if (error.code === GateErrorCode.HEARTBEAT_EXPIRED) {
      console.error('Heartbeat token expired - will retry automatically');
    }
  }
}

Heartbeat Manager API

The heartbeat manager is internal to the SDK, but you can access it if needed:

// Check if heartbeat is valid
const isValid = gate.heartbeatManager.isValid();

// Get current heartbeat token (if valid)
const token = gate.heartbeatManager.getToken();

// Update signer ID (called automatically when signer is known)
gate.heartbeatManager.updateSignerId('new-signer-id');

// Stop heartbeat refresher (e.g., on shutdown)
gate.heartbeatManager.stop();

Note: The heartbeat manager automatically updates the signerId when using the KMS wrapper, so manual updates are typically not needed.

Secret Rotation

For HMAC authentication, secret rotation is the customer's responsibility:

  1. Update environment variable with new secret
  2. SDK reads from config at runtime (no caching)
  3. Restart application to use new secret

The SDK does not cache secrets across process restarts unless the user explicitly does so.

Security

  • HTTPS Required: SDK validates HTTPS in production (localhost exception)
  • Secret Protection: Never logs secrets or API keys
  • Clock Skew: Configurable tolerance for timestamp validation
  • Replay Protection: Request ID + timestamp prevent replay attacks
  • Heartbeat Enforcement: All signing operations require valid heartbeat tokens

TypeScript Support

Full TypeScript definitions are included:

import type {
  DefenseEvaluateRequestV2,
  DefenseEvaluateResponseV2,
  GateDecision,
  StepUpStatusResponse,
  GateStepUpStatus,
  StepUpFinalResult,
} from '@blockintel/gate-sdk';

Examples

See examples directory for complete examples:

Publishing

  • Package versions are immutable once published (NPM does not allow overwriting a released version). Always bump the version before tagging a release.

See PUBLISHING.md for detailed publishing instructions.

Quick steps:

  1. Update version in package.json
  2. Create GitHub release tag
  3. GitHub Actions publishes to NPM automatically

License

MIT License - see LICENSE file.

Support

  • Documentation: https://docs.blockintelai.com
  • Issues: https://github.com/4KInc/blockintel-ai/issues
  • Email: [email protected]

Keywords

blockintel, gate, sdk, defense, crypto, security, transaction, evaluation