@castleio/sdk

中文文档
3.2.0发布于 2026/09/08MITTypeScriptESM + CJS近 30 天下载 5万

Castle SDK for Node

原始使用文档
文档目录21 节
  1. Requirements
  2. Installation
  3. Quick start
  4. Configuration
  5. Config options
  6. Multi-environment / multi-tenant
  7. Usage
  8. Risk
  9. Filter
  10. Log
  11. Lists & List Items
  12. Privacy (GDPR)
  13. Events (enterprise)
  14. Webhook signature verification
  15. Secure mode
  16. Advanced configuration
  17. Header allow/deny lists
  18. Client IP detection
  19. Errors
  20. Upgrading to 3.0.0
  21. License

Castle SDK for Node

Test Lint npm version

The official Node.js SDK for Castle. Castle analyzes user behavior in web and mobile apps to stop fraud before it happens.

This package is a thin, dependency-light wrapper around the Castle HTTP API. It exposes:

  • Risk assessment — POST /v1/risk, POST /v1/filter
  • Event logging — POST /v1/log (fire-and-forget, no verdict)
  • Lists & List Items — full CRUD + search + count + batch
  • Privacy / GDPR — POST and DELETE /v1/privacy/users (Article 15 & 17)
  • Events (enterprise) — search, group, and schema
  • Webhook signature verification — validate X-Castle-Signature
  • Secure mode — sign user IDs sent from the browser

A full list of supported events and the JSON shape of every payload is documented at https://reference.castle.io.

Requirements

  • Node.js >= 20 (the SDK uses the built-in global fetch / AbortSignal)
  • A Castle API secret

The package ships both ESM and CommonJS builds, so both import and require work out of the box.

Installation

npm install --save @castleio/sdk
yarn add @castleio/sdk

Quick start

import { Castle, ContextPrepareService } from '@castleio/sdk';

const castle = new Castle({ apiSecret: process.env.CASTLE_API_SECRET });

const context = ContextPrepareService.call(
  req,
  undefined,
  castle.configuration
);

const verdict = await castle.risk({
  type: '$login',
  status: '$succeeded',
  request_token: req.body.castle_request_token,
  user: { id: '12345', email: '[email protected]' },
  context,
});

switch (verdict.policy?.action) {
  case 'deny':
    // block the user
    break;
  case 'challenge':
    // send 2FA / additional verification
    break;
  default:
  // allow
}

With CommonJS:

const { Castle, ContextPrepareService } = require('@castleio/sdk');

ContextPrepareService.call(request, options, configuration) extracts the IP and headers that Castle needs from a Node request object. See Advanced configuration for how header allow/deny lists and proxy chains are resolved.

Configuration

The library is configured per Castle instance:

import { Castle, FailoverStrategy } from '@castleio/sdk';

const castle = new Castle({
  apiSecret: process.env.CASTLE_API_SECRET,

  // Automatic verdict from `risk`/`filter` when Castle is unreachable or
  // times out. One of FailoverStrategy.allow (default), .deny, .challenge.
  failoverStrategy: FailoverStrategy.allow,

  // Request timeout in milliseconds before the failover strategy kicks in.
  timeout: 1500,

  // Logs Castle API requests and responses; must respond to `info`.
  logger: console,
});

Config options

OptionTypeDefaultDescription
apiSecretstringAPI secret from the dashboard.
timeoutnumber1500Time in ms before returning the failover strategy.
failoverStrategyFailoverStrategyFailoverStrategy.allowAutomatic risk response on timeout: allow, deny, or challenge.
loggerobjectLogs Castle API requests/responses; must respond to info.
doNotTrackbooleanfalseWhen true, suppresses all requests and triggers failover on risk. Useful in development and testing.
allowlistedstring[][]Strict header allow-list (see Header allow/deny lists).
denylistedstring[][]Headers to always scrub, in addition to the always-blocked Cookie / Authorization.
ipHeadersstring[][]Custom headers to read the client IP from (see Client IP detection).
trustedProxies(string | RegExp)[][]Known proxy IPs (strings or regexes). Pick this or trustedProxyDepth, never both.
trustedProxyDepthnumber0Number of known trusted proxies in the chain. Pick this or trustedProxies, never both.
trustProxyChainbooleanfalseTrust the entire X-Forwarded-For chain. Warning: promiscuous — a malicious proxy can spoof the client IP.
baseUrlstringhttps://api.castle.io/v1Base Castle API URL.

Multi-environment / multi-tenant

Each Castle instance carries its own configuration, so multiple environments or tenants are just multiple instances:

const castleTenantA = new Castle({
  apiSecret: process.env.CASTLE_API_SECRET_TENANT_A,
});
const castleTenantB = new Castle({
  apiSecret: process.env.CASTLE_API_SECRET_TENANT_B,
});

Usage

All endpoints are async methods on a Castle instance.

Risk

Evaluates high-risk events (logins, registrations, password resets, transactions). Returns a verdict (policy.action) plus risk scores and signals.

await castle.risk({
  type: '$login',
  status: '$succeeded',
  request_token: req.body.castle_request_token,
  user: { id: '12345', email: '[email protected]' },
  context,
});

Filter

Blocks bots and bad traffic early in the chain (typically registration). Same response shape as Risk.

await castle.filter({
  type: '$registration',
  status: '$attempted',
  request_token: req.body.castle_request_token,
  params: { email: '[email protected]' },
  context,
});

Log

Fire-and-forget event logging; no verdict is returned. Useful for events that should be visible in the dashboard but don't need a real-time decision.

await castle.log({
  type: '$profile_update',
  status: '$succeeded',
  user: { id: '12345' },
  context,
});

Lists & List Items

Lists let you organize users, IPs, transactions, or any custom property and use them in policies as allow/deny lists:

const list = await castle.createList({
  name: 'Trusted IPs',
  color: 'green',
  primary_field: 'ip.address',
});

await castle.createListItem({
  list_id: list.id,
  primary_value: '1.2.3.4',
  author: { type: '$analyst_email', identifier: '[email protected]' },
});

await castle.searchListItems({
  list_id: list.id,
  filters: [{ field: 'primary_value', op: '$eq', value: '1.2.3.4' }],
});

Available methods: createList, fetchAllLists, fetchList, updateList, deleteList, searchLists, createListItem, batchUpdateListItems, fetchListItem, searchListItems, countListItems, updateListItem, archiveListItem, unarchiveListItem.

Privacy (GDPR)

Supports GDPR Articles 15 (right of access) and 17 (right to be forgotten) via /v1/privacy/users. Both take an identifier and identifier_type ($id or $email):

await castle.requestUserData({
  identifier: '[email protected]',
  identifier_type: '$email',
});

await castle.deleteUserData({
  identifier: 'user_42',
  identifier_type: '$id',
});

Events (enterprise)

Query, group, and inspect the schema of stored events. Use queryEvents, groupEvents, and eventsSchema:

await castle.queryEvents({
  filters: [
    /* ... */
  ],
});
await castle.groupEvents({
  filters: [],
  group_by: { fields: [] },
  columns: [],
});
await castle.eventsSchema();

searchEvents and getEventsSchema are also available as aliases of queryEvents and eventsSchema.

Webhook signature verification

Castle signs every webhook with an X-Castle-Signature header. Verify it against the raw request body before trusting the payload — verifyWebhookSignature throws a WebhookVerificationError on mismatch:

import express from 'express';
import { WebhookVerificationError } from '@castleio/sdk';

const app = express();

// The signature is computed over the raw body, so capture it as a Buffer.
app.post(
  '/castle/webhooks',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    try {
      castle.verifyWebhookSignature(req.body, req.get('x-castle-signature'));
    } catch (err) {
      if (err instanceof WebhookVerificationError) {
        return res.sendStatus(400);
      }
      throw err;
    }

    const event = JSON.parse(req.body.toString());
    // signature is valid; proceed
    res.sendStatus(200);
  }
);

Secure mode

Secure mode signs the user id server-side so it can't be tampered with in the browser. Pass the signature to the client-side castle.js SDK:

const signature = castle.secureModeSignature('user_123');

The signature is a hex HMAC-SHA256 of the user id keyed with your API secret.

Advanced configuration

The defaults are good for most deployments. The options below only matter if you have a non-trivial proxy chain or strict header policies.

Header allow/deny lists

By default the SDK sends every HTTP header except Cookie and Authorization. Castle uses these headers to fingerprint the request, so the broader the better.

import { DEFAULT_ALLOWLIST } from '@castleio/sdk';

const castle = new Castle({
  apiSecret: process.env.CASTLE_API_SECRET,

  // Always-blocked headers (in addition to Cookie/Authorization).
  denylisted: ['X-Internal-Header'],

  // Strict allow-list mode. Headers outside the list are sent with scrubbed
  // values, except User-Agent which is always preserved. If you must use an
  // allow list, we recommend starting from the curated default.
  allowlisted: DEFAULT_ALLOWLIST,
});

Header names are case-insensitive and accept both _ and - as separators. A leading HTTP_ prefix is stripped automatically.

Client IP detection

Castle needs the original client IP, not the IP of your proxy or load balancer. The SDK reads X-Forwarded-For and Remote-Addr by default; pick one of the strategies below depending on your infrastructure:

const castle = new Castle({
  apiSecret: process.env.CASTLE_API_SECRET,

  // 1. Custom header (e.g. Cloudflare's Cf-Connecting-Ip).
  ipHeaders: ['Cf-Connecting-Ip'],

  // 2. Static, known proxy IPs.
  trustedProxies: ['10.0.0.1'],

  // 3. Ephemeral proxies but known chain depth.
  trustedProxyDepth: 2,

  // 4. Last resort: trust the entire X-Forwarded-For chain.
  // Warning: vulnerable to header spoofing if a malicious proxy is in path.
  trustProxyChain: false,
});

Pick either trustedProxies or trustedProxyDepth, never both. Private/loopback ranges in the exported TRUSTED_PROXIES constant are always considered trusted.

Errors

All HTTP/API exceptions inherit from APIError. ConfigurationError (setup problems) and WebhookVerificationError (webhook signature mismatch) extend Error directly. The most useful ones:

ClassRaised when
ConfigurationErrorThe SDK is misconfigured (missing API secret, bad URL).
BadRequestError400 response.
UnauthorizedError401 — bad API secret.
ForbiddenError403 response.
NotFoundError404 response.
InvalidParametersError422 response with validation details.
InvalidRequestTokenError422 — the request_token is missing or invalid.
RateLimitError429 — back off and retry.
PaymentRequiredError402 response.
InternalServerError5xx response from Castle.
WebhookVerificationErrorA webhook signature did not match.

The full list lives in src/errors.ts.

Upgrading to 3.0.0

3.0.0 is a modernization release. The public API (the Castle class and its methods) is unchanged — most projects can upgrade without code changes.

What changed:

  • Legacy endpoints removed. authenticate, track, and the device endpoints (getDevice, getDevicesForUser, approveDevice, reportDevice) are gone — use risk, filter, and log instead. The matching payload types (Payload, DevicePayload, UserDevicePayload, AuthenticateResult) were removed too.
    • If you previously used reportDevice / approveDevice, manage device approvals and reports with the Lists API instead (e.g. createListItem / archiveListItem). See Reporting devices using Lists.
  • Node.js >= 20 is now required. The SDK uses the runtime's built-in fetch and AbortSignal instead of node-fetch / abort-controller, which are no longer dependencies.
  • Dual ESM + CommonJS build. The package now exposes a proper exports map with separate import and require entry points and type definitions. Deep imports into dist/ were never supported and remain unsupported; import from the package root (@castleio/sdk).
  • The lodash.* micro-dependencies were removed in favor of native JavaScript; this is internal and does not affect behavior.

If you previously relied on a custom overrideFetch, it still works — pass any fetch-compatible implementation.

License

Available as open source under the terms of the MIT License.

版本与发布记录

@castleio/sdk 共发布 29 个版本,首次发布于 2018/12/20;过去 12 个月发布了 3 个版本。

版本发布日期距今
3.2.0latest2026/09/082 个月前
3.1.02026/09/082 个月前
3.0.02026/06/065 个月前
2.3.32025/08/121.2 年前
2.3.22025/08/121.2 年前
2.3.12025/07/181.3 年前

关于 @castleio/sdk 的常见问题

@castleio/sdk 是什么?

Castle SDK for Node

如何安装 @castleio/sdk?

在项目目录运行 npm install @castleio/sdk,或使用 pnpm add / yarn add / bun add。

@castleio/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@castleio/sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@castleio/sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@castleio/sdk 可以商用吗?

@castleio/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@castleio/sdk 的下载量有多少?

根据 npm 官方下载统计,@castleio/sdk 在 2026/10/09 – 2026/11/07 这 30 天内被下载 49,528 次,日均约 1,651 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@castleio/sdk 还在维护吗?

最新版本 3.2.0 发布于 2026/09/08(2 个月前);至今共发布 29 个版本,其中过去 12 个月发布了 3 个;首次发布于 2018/12/20。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。