@trustsig/server
v2.3.0
Published
Node.js and Edge SDK for TrustSig — token verification and local decryption
Maintainers
Readme
@trustsig/server
Node.js and Edge SDK for TrustSig bot protection. Handles token verification and local decryption. Requires Node.js 18+.
Installation
npm install @trustsig/serverUsage
import { TrustSig } from '@trustsig/server';
const ts = new TrustSig({ secretKey: process.env.TRUSTSIG_SECRET_KEY });
const token = request.headers.get('X-TrustSig-Response'); // string | null
const result = await ts.verifyRemote(token);
// Allow-list check: fail-closed. Every failure mode resolves to BLOCK.
if (result.action !== 'ALLOW') {
throw new Error(`Access denied (${result.error ?? result.action})`);
}The result contract
verifyLocal and verifyRemote return the same shape and never throw —
they always resolve to a decision:
| Field | Type | Notes |
| --- | --- | --- |
| action | 'ALLOW' \| 'CHALLENGE' \| 'BLOCK' | All failures normalise to BLOCK. |
| blocked | boolean | Equals action !== 'ALLOW'. |
| error | TrustSigErrorCode \| null | null on success; otherwise TOKEN_MISSING, TOKEN_EXPIRED, TOKEN_REUSED, CRYPTO_FAIL, API_FAIL, or MALFORMED_RESPONSE. |
| is_bot | boolean | |
| score | number | 0–100, higher = more suspicious. |
| request_id | string | Correlation id; usable for an app-side replay cache. |
| factors | string[] | Contributing signals. |
| evidence | Record<string, unknown> | Raw signal detail. |
| site_key | string | |
Gate access with
action !== 'ALLOW'orblocked. Never match the oldBLOCK_CRYPTO_FAIL/BLOCK_API_FAILstrings — they no longer exist.
Verification Methods
verifyLocal(token)— decrypts locally with ChaCha20-Poly1305. Zero latency, no network. Checks decryptability, age, and (by default) per-token reuse via an in-process cache. See Replay protection below for the cap and how to tune or disable it.verifyRemote(token)— validates against the TrustSig Edge API. Replay-resistant via the server-side nonce, full telemetry, configurable timeout. Recommended for authentication, payments, and account changes.
Replay protection
verifyLocal ships with a small in-process cache that hashes every accepted
token (SHA-256) and caps how many times the same token may be verified. By
default the same token is accepted up to 4 times; the 5th call resolves
to action: 'BLOCK' with error: 'TOKEN_REUSED'. Entries auto-evict at the
token's own expiry (issued_at + maxTokenAgeSeconds, default 300s / 5
minutes), so memory growth is bounded by the in-flight token set.
// Defaults: replayProtection on, 4 uses per token.
const ts = new TrustSig({ secretKey });
// Tighter — single-use tokens (server-rendered checkout, OTP confirmation, …).
const oneShot = new TrustSig({ secretKey, maxUsesPerToken: 1 });
// Looser — keep tracking but raise the cap.
const generous = new TrustSig({ secretKey, maxUsesPerToken: 50 });
// Off — accept the same token unlimited times within its age window.
const noReplay = new TrustSig({ secretKey, replayProtection: false });
// Tests / manual operator reset.
ts.clearReplayCache();Runtime requirement. The cache lives in process memory, so it only enforces the cap inside a long-lived server: a Node process, a container, a fly.io machine, a Cloud Run instance with
min-instances >= 1, a worker on a queue. It is not preserved across cold starts or shared between horizontally scaled instances. In per-request serverless runtimes (each invocation spawns a fresh process) the cache always starts empty and the cap cannot be enforced — setreplayProtection: falseto drop the overhead, or callverifyRemote()which validates the nonce server-side.
Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| secretKey | string | required | Secret key. Server-side only — never expose to the browser. |
| env | PROD \| DEMO \| DEV | PROD | Selects the default endpoint host. |
| endpoint | string | env-derived | Override the verification endpoint. |
| timeoutMs | number | 5000 | verifyRemote network timeout; abort fails closed. |
| maxTokenAgeSeconds | number | 300 | Max accepted token age for verifyLocal. Also the eviction TTL for replay-cache entries. |
| clockSkewSeconds | number | 30 | Tolerance for future-dated tokens. |
| replayProtection | boolean | true | Track token hashes in process memory and cap reuse via verifyLocal. Set false in serverless runtimes. |
| maxUsesPerToken | number | 4 | Maximum times the same token may pass verifyLocal before TOKEN_REUSED. Ignored when replayProtection is false. |
