blockintel-gate-sdk 0.4.6 → 0.4.8
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 +50 -2
- package/dist/index.cjs +33 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/dist/pilot/index.cjs +1 -1
- package/dist/pilot/index.cjs.map +1 -1
- package/dist/pilot/index.js +1 -1
- package/dist/pilot/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Production-grade TypeScript/Node.js SDK for [BlockIntel Gate](https://blockintelai.com) Hot Path API.
|
|
4
4
|
|
|
5
|
+
Gate intercepts AWS KMS signing operations before cryptographic signatures are produced. Every transaction is evaluated against policy (denylist, velocity caps, value thresholds, signer allowlist) in <50ms. If policy says no, the key never signs.
|
|
6
|
+
|
|
7
|
+
**SHADOW mode** — zero production risk. Gate evaluates every transaction and logs WOULD_BLOCK decisions, but never blocks. Drop it into your existing signing flow in minutes and collect 14 days of data before enforcing anything.
|
|
8
|
+
|
|
5
9
|
## Installation
|
|
6
10
|
|
|
7
11
|
```bash
|
|
@@ -21,9 +25,51 @@ npm install @blockintel/gate-sdk
|
|
|
21
25
|
- **signingContext**: Hot Path requires `actorPrincipal` and `signerId`. The SDK defaults them when missing (`gate-sdk-client` or from `signingContext.signerId`).
|
|
22
26
|
- **ESM**: HMAC and SHA-256 use `node:crypto` (no `require('crypto')`), so the SDK works in ESM (`"type": "module"`) and in bundled canary apps.
|
|
23
27
|
|
|
24
|
-
## Quick Start
|
|
28
|
+
## Quick Start — 3 Steps
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
```typescript
|
|
31
|
+
// 1. Install
|
|
32
|
+
// npm install @blockintel/gate-sdk
|
|
33
|
+
|
|
34
|
+
// 2. Initialize (API key — simplest auth for getting started)
|
|
35
|
+
import { GateClient } from '@blockintel/gate-sdk';
|
|
36
|
+
|
|
37
|
+
const gate = new GateClient({
|
|
38
|
+
baseUrl: process.env.GATE_BASE_URL!,
|
|
39
|
+
tenantId: process.env.GATE_TENANT_ID!,
|
|
40
|
+
auth: { mode: 'apiKey', apiKey: process.env.GATE_API_KEY! },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 3. Wrap your signing call
|
|
44
|
+
const decision = await gate.evaluate({
|
|
45
|
+
txIntent: {
|
|
46
|
+
from: '0xYourAddress',
|
|
47
|
+
to: '0xDestination',
|
|
48
|
+
value: '1000000000000000000', // 1 ETH in wei
|
|
49
|
+
chainId: 1,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// In SHADOW mode: always ALLOW, but reasonCodes shows what WOULD_BLOCK
|
|
54
|
+
console.log(decision.decision); // "ALLOW"
|
|
55
|
+
console.log(decision.reasonCodes); // ["DENYLIST"] if policy would have blocked
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Get a free tenant at [blockintelai.com](https://blockintelai.com). For production use, switch to HMAC authentication — see the [Production Setup — HMAC Authentication](#production-setup--hmac-authentication) section below.
|
|
59
|
+
|
|
60
|
+
## Why Gate
|
|
61
|
+
|
|
62
|
+
**The problem:** Every `kms:Sign` call is unrestricted by default. Any process with valid IAM permissions can sign any transaction to any address for any amount. A compromised credential is a blank check.
|
|
63
|
+
|
|
64
|
+
**What Gate adds:**
|
|
65
|
+
- **Pre-signature policy evaluation** — denylist, value thresholds, velocity caps, signer allowlist
|
|
66
|
+
- **Cryptographic receipts** — every decision signed with HMAC + RSA, verifiable by third parties
|
|
67
|
+
- **SHADOW mode** — collect 14 days of data with zero production risk before enforcing
|
|
68
|
+
- **Enforcement ladder** — SHADOW → SOFT_ENFORCE → HARD_KMS_GATEWAY (IAM-level, app loses kms:Sign)
|
|
69
|
+
|
|
70
|
+
**Who uses it:** Crypto exchanges and custodians who need independently verifiable evidence of signing controls — for insurance carriers, compliance auditors (VARA, NYDFS), and internal governance.
|
|
71
|
+
|
|
72
|
+
## Production Setup — HMAC Authentication
|
|
27
73
|
|
|
28
74
|
```typescript
|
|
29
75
|
import { GateClient } from '@blockintel/gate-sdk';
|
|
@@ -180,6 +226,8 @@ console.log('Elapsed time:', result.elapsedMs, 'ms');
|
|
|
180
226
|
|
|
181
227
|
## Configuration
|
|
182
228
|
|
|
229
|
+
> **SHADOW mode is the default.** `onConnectionFailure: 'FAIL_OPEN'` means Gate never blocks your signing operations — even if Gate itself is unreachable. You get full visibility into what would have been blocked without any production risk. Switch to `FAIL_CLOSED` only when you're ready to enforce.
|
|
230
|
+
|
|
183
231
|
### `GateClientConfig`
|
|
184
232
|
|
|
185
233
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -1217,7 +1217,7 @@ async function handleSignCommand(command, originalClient, gateClient, options) {
|
|
|
1217
1217
|
if (options.mode === "dry-run") {
|
|
1218
1218
|
return await originalClient.send(new clientKms.SignCommand(command));
|
|
1219
1219
|
}
|
|
1220
|
-
const GATEWAY_STAGES = ["HARD_KMS_GATEWAY", "HARD_GCP_GATEWAY"
|
|
1220
|
+
const GATEWAY_STAGES = ["HARD_KMS_GATEWAY", "HARD_GCP_GATEWAY"];
|
|
1221
1221
|
const currentStage = gateClient.heartbeatManager?.getAdoptionStage?.();
|
|
1222
1222
|
if (currentStage && GATEWAY_STAGES.includes(currentStage)) {
|
|
1223
1223
|
emitMetric(options.metricsSink, "sign_success_total", labels);
|
|
@@ -3010,7 +3010,38 @@ var GcpKmsSigner = class {
|
|
|
3010
3010
|
if (!this.config.credentials) {
|
|
3011
3011
|
throw new Error("GCP credentials not configured");
|
|
3012
3012
|
}
|
|
3013
|
-
|
|
3013
|
+
const creds = typeof this.config.credentials === "string" ? JSON.parse(this.config.credentials) : this.config.credentials;
|
|
3014
|
+
if (!creds.client_email || !creds.private_key) {
|
|
3015
|
+
throw new Error("GCP credentials must contain client_email and private_key");
|
|
3016
|
+
}
|
|
3017
|
+
const crypto = await import('crypto');
|
|
3018
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3019
|
+
const header = Buffer.from(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString("base64url");
|
|
3020
|
+
const payload = Buffer.from(JSON.stringify({
|
|
3021
|
+
iss: creds.client_email,
|
|
3022
|
+
scope: "https://www.googleapis.com/auth/cloudkms",
|
|
3023
|
+
aud: "https://oauth2.googleapis.com/token",
|
|
3024
|
+
iat: now,
|
|
3025
|
+
exp: now + 3600
|
|
3026
|
+
})).toString("base64url");
|
|
3027
|
+
const sigInput = `${header}.${payload}`;
|
|
3028
|
+
const sign = crypto.createSign("RSA-SHA256");
|
|
3029
|
+
sign.update(sigInput);
|
|
3030
|
+
const sig = sign.sign(creds.private_key, "base64url");
|
|
3031
|
+
const jwt = `${sigInput}.${sig}`;
|
|
3032
|
+
const tokenResponse = await fetch("https://oauth2.googleapis.com/token", {
|
|
3033
|
+
method: "POST",
|
|
3034
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
3035
|
+
body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`
|
|
3036
|
+
});
|
|
3037
|
+
if (!tokenResponse.ok) {
|
|
3038
|
+
const errText = await tokenResponse.text();
|
|
3039
|
+
throw new Error(`GCP SA token exchange failed: ${tokenResponse.status} ${errText}`);
|
|
3040
|
+
}
|
|
3041
|
+
const data = await tokenResponse.json();
|
|
3042
|
+
this.accessToken = data.access_token;
|
|
3043
|
+
this.tokenExpiry = Date.now() + data.expires_in * 1e3;
|
|
3044
|
+
return data.access_token;
|
|
3014
3045
|
}
|
|
3015
3046
|
}
|
|
3016
3047
|
/**
|