Production-grade TypeScript/Node.js SDK for BlockIntel Gate Hot Path
npm install blockintel-gate-sdkProduction-grade TypeScript/Node.js SDK for BlockIntel Gate Hot Path API.
``bash`
npm install @blockintel/gate-sdk
- Node.js >= 18.0.0 (uses global fetch API)
- TypeScript >= 5.0.0 (optional, for type definitions)
- 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.actorPrincipal
- signingContext: Hot Path requires and signerId. The SDK defaults them when missing (gate-sdk-client or from signingContext.signerId).node:crypto
- ESM: HMAC and SHA-256 use (no require('crypto')), so the SDK works in ESM ("type": "module") and in bundled canary apps.
`typescript
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);
}
`
`typescript
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',
},
});
`
For local development and testing, use Gate Local - a Docker container that emulates the Gate Hot Path:
`bash`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:
`typescript
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.
`typescript
// 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 tenantEXPIRED
- → TTL exceeded (deterministic), even if DynamoDB TTL has not deleted the item yetPENDING
- → waiting for external approvalAPPROVED | DENIED
- → terminal states
`typescript`
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/
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.`
}
`bashRequired
GATE_BASE_URL=https://gate.blockintelai.com
GATE_TENANT_ID=your-tenant-id
API Reference
$3
Evaluate a transaction defense request.
Parameters:
-
req: DefenseEvaluateRequestV2 - Transaction and signing context
- opts?: { requestId?: string } - Optional request ID (auto-generated if not provided)Returns:
PromiseResponse:
`typescript
{
decision: 'ALLOW' | 'BLOCK' | 'REQUIRE_STEP_UP';
reasonCodes: string[];
policyVersion?: string;
correlationId?: string;
stepUp?: {
requestId: string;
ttlSeconds?: number;
};
}
`$3
Get current step-up status.
Parameters:
-
args: { requestId: string; tenantId?: string }Returns:
PromiseStatus 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$3
Poll step-up status until decision is reached or timeout.
Parameters:
-
args: { requestId: string; maxWaitMs?: number; intervalMs?: number }Returns:
PromiseError Handling
The SDK uses custom error types:
`typescript
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 parametersAuthentication
$3
The SDK implements HMAC v1 signing for secure authentication:
Signing String:
`
v1\n
\n
\n
\n
\n
\n
\n
\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$3
For simpler onboarding, use API key authentication:
Headers:
-
X-API-KEY
- X-GATE-TENANT-ID
- X-GATE-REQUEST-ID
- X-GATE-TIMESTAMP-MSStep-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 EXPIREDImportant:
- 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 serverDegraded 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_LIMITEDHow to observe:
- Logs:
[GATE SDK] X-BlockIntel-Degraded: true (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.
$3
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$3
The heartbeat manager is automatically configured based on your
GateClientConfig:`typescript
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)
});
`$3
- 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$3
`typescript
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');
}
}
}
`$3
The heartbeat manager is internal to the SDK, but you can access it if needed:
`typescript
// 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:
`typescript
import type {
DefenseEvaluateRequestV2,
DefenseEvaluateResponseV2,
GateDecision,
StepUpStatusResponse,
GateStepUpStatus,
StepUpFinalResult,
} from '@blockintel/gate-sdk';
`Examples
See examples directory for complete examples:
- HMAC Authentication
- API Key Authentication
- Step-Up Flow
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`MIT License - see LICENSE file.
- Documentation: https://docs.blockintelai.com
- Issues: https://github.com/4KInc/blockintel-ai/issues
- Email: support@blockintelai.com
blockintel, gate, sdk, defense, crypto, security, transaction, evaluation