@sentinelsup/sdk 0.1.0

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.
Files changed (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/index.d.ts +60 -0
  4. package/index.js +140 -0
  5. package/package.json +42 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sentinel Edge Networks LTD
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @sentinelsup/sdk
2
+
3
+ Official Node.js SDK for [Sentinel](https://sntlhq.com) — real-time fraud detection that flags VPNs, residential proxies, antidetect browsers, and AI bots in under 40 ms.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@sentinelsup/sdk.svg)](https://www.npmjs.com/package/@sentinelsup/sdk)
6
+ [![license](https://img.shields.io/npm/l/@sentinelsup/sdk.svg)](./LICENSE)
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @sentinelsup/sdk
12
+ ```
13
+
14
+ Zero dependencies. Works on Node 14+, Bun, Deno, Cloudflare Workers, and Vercel Edge (wherever `fetch` exists).
15
+
16
+ ## Quick start
17
+
18
+ ```js
19
+ const Sentinel = require('@sentinelsup/sdk');
20
+
21
+ const sentinel = new Sentinel({ apiKey: process.env.SENTINEL_KEY });
22
+
23
+ const result = await sentinel.evaluate({
24
+ token: req.body.sentinelToken // from the frontend SDK
25
+ });
26
+
27
+ if (result.isSuspicious) {
28
+ return res.status(403).json({ error: 'blocked' });
29
+ }
30
+
31
+ // Safe — let the request through
32
+ ```
33
+
34
+ Get a free API key (no credit card) at [sntlhq.com/signup](https://sntlhq.com/signup).
35
+
36
+ ## What you get back
37
+
38
+ ```ts
39
+ {
40
+ isSuspicious: boolean, // combined network + device verdict
41
+ details: {
42
+ ip: '45.33.32.156',
43
+ cc: 'US',
44
+ vpn: false,
45
+ proxied: true, // residential proxy detected
46
+ dch: false, // datacenter
47
+ anon: false,
48
+ service: 'BrightData'
49
+ },
50
+ deviceIntel: { // null unless you pass fingerprintEventId
51
+ visitorId: 'abc123...',
52
+ browserTampering: true, // antidetect browser detected
53
+ botDetected: false,
54
+ incognito: false,
55
+ virtualMachine: false,
56
+ emulator: false
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Frontend setup
62
+
63
+ Add the client-side SDK to your frontend so Sentinel can collect the token:
64
+
65
+ ```html
66
+ <script async src="https://fp.sntlhq.com/agent"></script>
67
+ ```
68
+
69
+ Read the token from the rendered form field and send it to your backend:
70
+
71
+ ```js
72
+ const token = document.querySelector('input[name="monocle"]').value;
73
+ fetch('/checkout', { method: 'POST', body: JSON.stringify({ sentinelToken: token }) });
74
+ ```
75
+
76
+ ## Examples
77
+
78
+ ### Stripe Checkout — block card testing
79
+
80
+ ```js
81
+ const Sentinel = require('@sentinelsup/sdk');
82
+ const stripe = require('stripe')(process.env.STRIPE_KEY);
83
+ const sentinel = new Sentinel({ apiKey: process.env.SENTINEL_KEY });
84
+
85
+ app.post('/checkout', async (req, res) => {
86
+ const { isSuspicious } = await sentinel.evaluate({ token: req.body.sentinelToken });
87
+ if (isSuspicious) return res.status(403).json({ error: 'declined' });
88
+
89
+ const intent = await stripe.paymentIntents.create({ /* ... */ });
90
+ res.json({ clientSecret: intent.client_secret });
91
+ });
92
+ ```
93
+
94
+ ### Signup — block fake Google sign-ins
95
+
96
+ ```js
97
+ app.post('/auth/google', async (req, res) => {
98
+ const { credential, sentinelToken } = req.body;
99
+ const ticket = await googleClient.verifyIdToken({ idToken: credential });
100
+
101
+ const result = await sentinel.evaluate({ token: sentinelToken });
102
+ if (result.isSuspicious) return res.status(403).json({ error: 'signup_blocked' });
103
+
104
+ await createUser(ticket.getPayload().email, result.deviceIntel?.visitorId);
105
+ });
106
+ ```
107
+
108
+ ### Custom policy with `shouldBlock`
109
+
110
+ ```js
111
+ // Only block when we see both residential proxy AND browser tampering
112
+ const blocked = await sentinel.shouldBlock(
113
+ { token },
114
+ r => r.details.proxied && r.deviceIntel?.browserTampering
115
+ );
116
+ ```
117
+
118
+ ## API
119
+
120
+ ### `new Sentinel({ apiKey, endpoint?, timeoutMs? })`
121
+
122
+ | Option | Type | Default | Description |
123
+ |--------|------|---------|-------------|
124
+ | `apiKey` | string | required | Your key starting with `sk_live_` |
125
+ | `endpoint` | string | `https://sntlhq.com` | Override base URL |
126
+ | `timeoutMs` | number | `5000` | Per-request timeout |
127
+
128
+ ### `sentinel.evaluate({ token, fingerprintEventId? })`
129
+
130
+ Returns `EvaluateResult`. Throws `SentinelError` on network/API failure — the error carries `.status` and `.body`.
131
+
132
+ ### `sentinel.shouldBlock({ token, fingerprintEventId? }, predicate?)`
133
+
134
+ Convenience: runs `evaluate()` and returns a boolean. Default predicate is `r => r.isSuspicious`. Pass your own to build custom policies.
135
+
136
+ ## Rate limits
137
+
138
+ Free tier: **1,000 requests/hour** per API key. No monthly cap, no credit card. Upgrade at [sntlhq.com](https://sntlhq.com) when you need more.
139
+
140
+ ## TypeScript
141
+
142
+ Full types ship with the package. Importing `Sentinel` gives you the class plus `EvaluateResult`, `DeviceIntel`, `EvaluateDetails`, and `SentinelError` types.
143
+
144
+ ```ts
145
+ import Sentinel, { EvaluateResult } from '@sentinelsup/sdk';
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT © Sentinel Edge Networks LTD
151
+
152
+ ## Links
153
+
154
+ - Website — [sntlhq.com](https://sntlhq.com)
155
+ - API docs — [sntlhq.com/api](https://sntlhq.com/api)
156
+ - Blog — [sntlhq.com/blog](https://sntlhq.com/blog)
157
+ - X / Twitter — [@SentinelSup](https://x.com/SentinelSup)
package/index.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ export interface EvaluateDetails {
2
+ ip: string;
3
+ cc: string;
4
+ vpn: boolean;
5
+ proxied: boolean;
6
+ dch: boolean;
7
+ anon: boolean;
8
+ crawler?: boolean;
9
+ service?: string;
10
+ }
11
+
12
+ export interface DeviceIntel {
13
+ visitorId: string | null;
14
+ browserTampering: boolean;
15
+ botDetected: boolean;
16
+ vpnDetected: boolean;
17
+ proxyDetected: boolean;
18
+ torDetected: boolean;
19
+ ipBlocklisted: boolean;
20
+ incognito: boolean;
21
+ virtualMachine: boolean;
22
+ emulator: boolean;
23
+ tamperingScore?: number;
24
+ }
25
+
26
+ export interface EvaluateResult {
27
+ status: string;
28
+ isSuspicious: boolean;
29
+ details: EvaluateDetails;
30
+ deviceIntel: DeviceIntel | null;
31
+ }
32
+
33
+ export interface SentinelOptions {
34
+ /** Your Sentinel API key (starts with sk_live_). Get one free at https://sntlhq.com/signup */
35
+ apiKey: string;
36
+ /** Override the API base URL (default: https://sntlhq.com) */
37
+ endpoint?: string;
38
+ /** Per-request timeout in ms (default: 5000) */
39
+ timeoutMs?: number;
40
+ }
41
+
42
+ export interface EvaluateInput {
43
+ /** Client-side Sentinel token from the frontend SDK */
44
+ token: string;
45
+ /** Optional Fingerprint event id for device-layer signals */
46
+ fingerprintEventId?: string;
47
+ }
48
+
49
+ export class SentinelError extends Error {
50
+ status?: number;
51
+ body?: unknown;
52
+ }
53
+
54
+ export default class Sentinel {
55
+ constructor(opts: SentinelOptions);
56
+ evaluate(input: EvaluateInput): Promise<EvaluateResult>;
57
+ shouldBlock(input: EvaluateInput, predicate?: (r: EvaluateResult) => boolean): Promise<boolean>;
58
+ }
59
+
60
+ export { Sentinel };
package/index.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Sentinel Node.js SDK — thin, zero-dependency wrapper around the
3
+ * Sentinel fraud detection API at https://sntlhq.com/v1/evaluate.
4
+ *
5
+ * Usage:
6
+ * const Sentinel = require('@sentinel/sdk');
7
+ * const sentinel = new Sentinel({ apiKey: process.env.SENTINEL_KEY });
8
+ * const result = await sentinel.evaluate({ token });
9
+ * if (result.isSuspicious) return res.status(403).end();
10
+ */
11
+
12
+ const DEFAULT_ENDPOINT = 'https://sntlhq.com';
13
+
14
+ class SentinelError extends Error {
15
+ constructor(message, { status, body } = {}) {
16
+ super(message);
17
+ this.name = 'SentinelError';
18
+ this.status = status;
19
+ this.body = body;
20
+ }
21
+ }
22
+
23
+ class Sentinel {
24
+ /**
25
+ * @param {object} opts
26
+ * @param {string} opts.apiKey — your Sentinel API key (starts with sk_live_)
27
+ * @param {string} [opts.endpoint] — override the default API base URL (for testing)
28
+ * @param {number} [opts.timeoutMs=5000] — per-request timeout
29
+ */
30
+ constructor(opts) {
31
+ if (!opts || typeof opts.apiKey !== 'string' || !opts.apiKey) {
32
+ throw new SentinelError('Sentinel: apiKey is required. Get one free at https://sntlhq.com/signup');
33
+ }
34
+ this.apiKey = opts.apiKey;
35
+ this.endpoint = (opts.endpoint || DEFAULT_ENDPOINT).replace(/\/$/, '');
36
+ this.timeoutMs = opts.timeoutMs || 5000;
37
+ }
38
+
39
+ /**
40
+ * Evaluate a visitor session for fraud signals.
41
+ *
42
+ * @param {object} input
43
+ * @param {string} input.token — Sentinel client-side token from the frontend SDK
44
+ * @param {string} [input.fingerprintEventId] — optional Fingerprint event id for device signals
45
+ * @returns {Promise<EvaluateResult>}
46
+ */
47
+ async evaluate({ token, fingerprintEventId } = {}) {
48
+ if (!token || typeof token !== 'string') {
49
+ throw new SentinelError('Sentinel.evaluate: token (client-side Sentinel token) is required');
50
+ }
51
+
52
+ const controller = new AbortController();
53
+ const abortTimer = setTimeout(() => controller.abort(), this.timeoutMs);
54
+
55
+ let res;
56
+ try {
57
+ res = await fetch(`${this.endpoint}/v1/evaluate`, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Authorization': `Bearer ${this.apiKey}`,
61
+ 'Content-Type': 'application/json'
62
+ },
63
+ body: JSON.stringify({ token, fingerprintEventId }),
64
+ signal: controller.signal
65
+ });
66
+ } catch (err) {
67
+ if (err.name === 'AbortError') {
68
+ throw new SentinelError(`Sentinel: request timed out after ${this.timeoutMs}ms`);
69
+ }
70
+ throw new SentinelError(`Sentinel: network error — ${err.message}`);
71
+ } finally {
72
+ clearTimeout(abortTimer);
73
+ }
74
+
75
+ let json;
76
+ try { json = await res.json(); } catch { json = null; }
77
+
78
+ if (!res.ok) {
79
+ throw new SentinelError(
80
+ `Sentinel: API returned ${res.status}${json && json.error ? ` — ${json.error}` : ''}`,
81
+ { status: res.status, body: json }
82
+ );
83
+ }
84
+
85
+ return json;
86
+ }
87
+
88
+ /**
89
+ * Convenience helper: returns true if the session should be blocked.
90
+ * Defaults to blocking when the API's own isSuspicious flag is set.
91
+ * Pass a custom predicate to build your own policy.
92
+ *
93
+ * @param {object} input — same as evaluate()
94
+ * @param {(r: EvaluateResult) => boolean} [predicate]
95
+ * @returns {Promise<boolean>}
96
+ */
97
+ async shouldBlock(input, predicate) {
98
+ const result = await this.evaluate(input);
99
+ return predicate ? !!predicate(result) : !!result.isSuspicious;
100
+ }
101
+ }
102
+
103
+ Sentinel.SentinelError = SentinelError;
104
+ module.exports = Sentinel;
105
+ module.exports.default = Sentinel;
106
+ module.exports.Sentinel = Sentinel;
107
+ module.exports.SentinelError = SentinelError;
108
+
109
+ /**
110
+ * @typedef {object} EvaluateResult
111
+ * @property {boolean} isSuspicious — true if network or device signals flag the session
112
+ * @property {EvaluateDetails} details — network / IP signals from Spur Monocle
113
+ * @property {DeviceIntel|null} deviceIntel — device signals if fingerprintEventId was supplied
114
+ */
115
+
116
+ /**
117
+ * @typedef {object} EvaluateDetails
118
+ * @property {string} ip
119
+ * @property {string} cc — 2-letter country code
120
+ * @property {boolean} vpn
121
+ * @property {boolean} proxied
122
+ * @property {boolean} dch — datacenter flag
123
+ * @property {boolean} anon
124
+ * @property {boolean} [crawler]
125
+ * @property {string} [service]
126
+ */
127
+
128
+ /**
129
+ * @typedef {object} DeviceIntel
130
+ * @property {string|null} visitorId
131
+ * @property {boolean} browserTampering
132
+ * @property {boolean} botDetected
133
+ * @property {boolean} vpnDetected
134
+ * @property {boolean} proxyDetected
135
+ * @property {boolean} torDetected
136
+ * @property {boolean} ipBlocklisted
137
+ * @property {boolean} incognito
138
+ * @property {boolean} virtualMachine
139
+ * @property {boolean} emulator
140
+ */
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@sentinelsup/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official Node.js SDK for Sentinel — real-time fraud detection, VPN/residential-proxy/antidetect-browser detection in under 40ms.",
5
+ "keywords": [
6
+ "fraud-detection",
7
+ "bot-detection",
8
+ "vpn-detection",
9
+ "proxy-detection",
10
+ "residential-proxy",
11
+ "antidetect-browser",
12
+ "stripe",
13
+ "shopify",
14
+ "chargeback",
15
+ "sentinel",
16
+ "sntlhq"
17
+ ],
18
+ "main": "index.js",
19
+ "types": "index.d.ts",
20
+ "files": [
21
+ "index.js",
22
+ "index.d.ts",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/sentinelsup/sentinel-node.git"
29
+ },
30
+ "homepage": "https://sntlhq.com",
31
+ "bugs": {
32
+ "url": "https://github.com/sentinelsup/sentinel-node/issues"
33
+ },
34
+ "author": "Sentinel Edge Networks LTD <support@sntlhq.com>",
35
+ "license": "MIT",
36
+ "engines": {
37
+ "node": ">=14"
38
+ },
39
+ "scripts": {
40
+ "test": "node --test test.js"
41
+ }
42
+ }