api-turnstile 0.1.7 → 0.1.9

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 CHANGED
@@ -1,35 +1,52 @@
1
- # API Turnstile — CAPTCHA-Free API Bot Protection & Abuse Prevention
1
+ # API Turnstile (Sentinel) Turnstile for APIs
2
2
 
3
3
  <div align="center">
4
4
  <img src="https://sentinel.risksignal.name.ng/sentinel-logo.png" alt="Sentinel Logo" width="120" />
5
- <h3>Turnstile for API</h3>
5
+ <h3>Your APIs are being abused. You just don't see it yet.</h3>
6
6
  <p>Cloudflare Turnstile protects browsers. <b>Sentinel protects APIs.</b></p>
7
7
  <p>
8
8
  <a href="https://www.npmjs.com/package/api-turnstile"><img src="https://img.shields.io/npm/v/api-turnstile?color=orange&style=flat-square" alt="NPM Version" /></a>
9
- <a href="https://github.com/00xf5/sentinelapinpm/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/api-turnstile?style=flat-square" alt="MIT License" /></a>
10
9
  <a href="https://sentinel.risksignal.name.ng"><img src="https://img.shields.io/badge/latency-<50ms-green?style=flat-square" alt="Latency" /></a>
11
10
  </p>
12
11
  </div>
13
12
 
14
13
  ---
15
14
 
16
- > **The first line of defense for modern APIs.**
17
- > **Block bots, scripts, credential stuffing, and automation attacks — without rate limits or CAPTCHAs.**
18
- > **API Turnstile is a high-velocity decision engine built specifically to protect API endpoints at the network edge.**
15
+ **Sentinel** is a high-velocity deterministic trust layer for modern APIs. It stops automated abuse, credential stuffing, and fake signups by analyzing **Infrastructure DNA** and enforcing **Behavioral Work Tokens** without ever showing a CAPTCHA to a human.
19
16
 
20
- ## What Is API Turnstile?
17
+ ## 🚫 Problems We Stop
18
+ - **Signup Flooding**: Thousands of fake accounts hitting your database.
19
+ - **Credential Stuffing**: Automated login attempts using leaked passwords.
20
+ - **API Scraping**: Competitors or AI agents draining your proprietary data.
21
+ - **Ghost Traffic Tax**: Unnecessary AWS/Cloud compute costs from non-human traffic.
21
22
 
22
- API Turnstile (Sentinel) is a deterministic trust layer for APIs. Unlike traditional WAFs that rely on static IP blocklists or user-hostile CAPTCHAs, Sentinel uses **Infrastructure Forensics** and **Behavioral Work Tokens (BWT)** to differentiate between legitimate users and automated scripts in real-time.
23
+ ## Global Edge Enforcement
24
+ Sentinel is built for the internet's edge. Deploy as a standard Node.js middleware or a **Global Edge Guard** on Cloudflare Workers / Vercel Edge.
23
25
 
24
- It allows you to maintain a frictionless user experience while effectively blocking 99.9% of automated threats, including sophisticated headless browsers and residential proxy rotation.
26
+ - **Fast-Path Matrix**: Instant identification of hosting/proxy infrastructure.
27
+ - **Edge Cache Support**: Sub-2ms rejection using Cloudflare KV or Vercel Edge Config.
28
+ - **Agentic Governance**: Specific profiles to identify and throttle AI Agents vs Humans.
25
29
 
26
- ## Architecture
30
+ ### Example: Cloudflare Worker Edge Enforcement
31
+ ```typescript
32
+ import { sentinelEdge } from 'api-turnstile';
27
33
 
28
- Sentinel operates on a three-tier defense matrix:
34
+ export default {
35
+ async fetch(request, env, ctx) {
36
+ const shield = sentinelEdge({
37
+ apiKey: env.SENTINEL_KEY,
38
+ cache: env.SENTINEL_KV, // Cloudflare KV Namespace
39
+ protect: ['/v1/*'],
40
+ profile: 'agentic' // Identify & throttle AI Agents
41
+ });
29
42
 
30
- 1. **Fast-Path Matrix (< 20ms)**: Instant vetting against a global ASN/IP reputation matrix (OVH, Hetzner, DigitalOcean, etc.).
31
- 2. **Behavioral Work Tokens (BWT)**: A cryptographic challenge-response system that escalatesPoW difficulty for suspicious IPs.
32
- 3. **Infrastructure Forensics**: Deep analysis of request signatures to detect Puppeteer, Playwright, curl, and VPN/Proxy masking.
43
+ const blockResponse = await shield(request, ctx);
44
+ if (blockResponse) return blockResponse;
45
+
46
+ return await fetch(request);
47
+ }
48
+ };
49
+ ```
33
50
 
34
51
  ---
35
52
 
@@ -128,6 +145,18 @@ Sentinel profiles tune the engine's heuristics based on the endpoint's value:
128
145
  | **`signup`** | Identity | Registration, Login, Forget Password. |
129
146
  | **`payments`** | Integrity | Checkout, Subscription, Payment Method Update. |
130
147
  | **`crypto`** | Pure Trust | Wallets, Faucets, On-Chain interactions. |
148
+ | **`agentic`** | AI Governance | LLM Agents, Scrapers, Automated Crawlers. |
149
+
150
+ ### Using the Agentic Profile
151
+ The `agentic` profile is designed to differentiate between human users and AI Agents (like GPT-5, Perplexity, etc.). When enabled, Sentinel provides granular signals that allow you to serve "Lite" or "Cached" content to bots while saving expensive compute for humans.
152
+
153
+ ```javascript
154
+ app.use(sentinel({
155
+ apiKey: '...',
156
+ protect: ['/data/*'],
157
+ profile: 'agentic'
158
+ }));
159
+ ```
131
160
 
132
161
  ---
133
162
 
@@ -188,6 +217,11 @@ BWT is our proprietary adaptive PoW system. When Sentinel identifies an "Unstabl
188
217
  - **Cloud Runtime**: Vercel Edge, Cloudflare Workers, AWS Lambda.
189
218
  - **Database**: Zero external DB dependencies (Decision Engine is managed).
190
219
 
220
+ ## Related
221
+
222
+ - [Sentinel API Security Platform](https://sentinel.risksignal.name.ng) — Managed API bot protection
223
+ - [Why CAPTCHAs Fail for APIs](https://sentinel.risksignal.name.ng/blog/captchas-fail-for-apis)
224
+
191
225
  ## License
192
226
 
193
227
  MIT © [Sentinel Security](https://sentinel.risksignal.name.ng)
@@ -0,0 +1,25 @@
1
+ import { SentinelConfig } from '../types';
2
+ /**
3
+ * Cloudflare Worker / Vercel Edge Adapter Options
4
+ */
5
+ export interface EdgeAdapterOptions extends SentinelConfig {
6
+ /**
7
+ * KV Namespace (for Cloudflare) or reference to Edge Config (for Vercel)
8
+ */
9
+ cache?: {
10
+ get: (key: string) => Promise<string | null>;
11
+ put: (key: string, value: string, options?: {
12
+ expirationTtl: number;
13
+ }) => Promise<void>;
14
+ };
15
+ /**
16
+ * Cache TTL in seconds (default: 300 / 5 minutes)
17
+ */
18
+ cacheTtl?: number;
19
+ }
20
+ /**
21
+ * Sentinel Edge Adapter (Cloudflare Workers / Vercel Edge Runtime)
22
+ * Optimized for sub-5ms local enforcement using KV/Edge caches.
23
+ */
24
+ export declare const sentinelEdge: (options: EdgeAdapterOptions) => (request: Request, context?: any) => Promise<import("undici-types").Response | null>;
25
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/edge/adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAoB,MAAM,UAAU,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACtD;;OAEG;IACH,KAAK,CAAC,EAAE;QACJ,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAC7C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;YAAE,aAAa,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3F,CAAC;IACF;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,kBAAkB,MAUtC,SAAS,OAAO,EAAE,UAAU,GAAG,oDAkFhD,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sentinelEdge = void 0;
4
+ const sentinel_1 = require("../client/sentinel");
5
+ /**
6
+ * Sentinel Edge Adapter (Cloudflare Workers / Vercel Edge Runtime)
7
+ * Optimized for sub-5ms local enforcement using KV/Edge caches.
8
+ */
9
+ const sentinelEdge = (options) => {
10
+ const client = new sentinel_1.SentinelClient(options.apiKey, options.endpoint, options.timeout, options.debug);
11
+ const ttl = options.cacheTtl || 300;
12
+ return async (request, context) => {
13
+ const url = new URL(request.url);
14
+ const path = url.pathname;
15
+ // Path checking logic (simplified for edge)
16
+ const isProtected = (p) => {
17
+ if (Array.isArray(options.protect)) {
18
+ return options.protect.some(pattern => {
19
+ if (pattern === p)
20
+ return true;
21
+ if (pattern.endsWith('*'))
22
+ return p.startsWith(pattern.slice(0, -1));
23
+ return false;
24
+ });
25
+ }
26
+ return !!options.protect[p];
27
+ };
28
+ if (!isProtected(path))
29
+ return null;
30
+ // Resolve IP
31
+ const ip = request.headers.get('cf-connecting-ip') ||
32
+ request.headers.get('x-real-ip') ||
33
+ request.headers.get('x-forwarded-for')?.split(',')[0].trim() ||
34
+ '127.0.0.1';
35
+ // 1. FAST PATH: Check Edge Cache
36
+ if (options.cache) {
37
+ try {
38
+ const cached = await options.cache.get(`sentinel:verdict:${ip}`);
39
+ if (cached === 'BLOCK') {
40
+ return new Response(JSON.stringify({ error: 'Access Denied', code: 'EDGE_BLOCK' }), {
41
+ status: 403,
42
+ headers: { 'Content-Type': 'application/json' }
43
+ });
44
+ }
45
+ if (cached === 'ALLOW')
46
+ return null;
47
+ }
48
+ catch (err) {
49
+ if (options.debug)
50
+ console.error('[Sentinel Edge] Cache Read Error:', err);
51
+ }
52
+ }
53
+ // 2. PRIMARY CHECK
54
+ try {
55
+ const decision = await client.check({
56
+ target: ip,
57
+ profile: options.profile || 'api',
58
+ trustToken: request.headers.get('x-sentinel-trust') || undefined
59
+ });
60
+ // Update Cache Asynchronously
61
+ if (options.cache && context?.waitUntil) {
62
+ context.waitUntil(options.cache.put(`sentinel:verdict:${ip}`, decision.allow ? 'ALLOW' : 'BLOCK', { expirationTtl: ttl }).catch(() => { }));
63
+ }
64
+ if (!decision.allow) {
65
+ return new Response(JSON.stringify({
66
+ error: 'Access Denied',
67
+ reason: decision.reason,
68
+ remediation: decision.remediation
69
+ }), {
70
+ status: 403,
71
+ headers: { 'Content-Type': 'application/json' }
72
+ });
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (options.debug)
77
+ console.error('[Sentinel Edge] Decision Error:', error);
78
+ if (options.fail === 'closed') {
79
+ return new Response(JSON.stringify({ error: 'Security System Unavailable' }), {
80
+ status: 403,
81
+ headers: { 'Content-Type': 'application/json' }
82
+ });
83
+ }
84
+ }
85
+ return null;
86
+ };
87
+ };
88
+ exports.sentinelEdge = sentinelEdge;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { sentinel } from './middleware/express';
2
2
  export { sentinelFastify } from './middleware/fastify';
3
- export { sentinelEdge } from './middleware/next';
3
+ export { sentinelEdge } from './edge/adapter';
4
4
  export { sentinelHono } from './middleware/hono';
5
5
  export { SentinelClient } from './client/sentinel';
6
6
  export type { SentinelConfig, SentinelDecision, SecurityProfile, ProtectionMode, FailStrategy, PathProtection, CheckParams, Verdict } from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,YAAY,EACR,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,WAAW,EACX,OAAO,EACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,YAAY,EACR,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,WAAW,EACX,OAAO,EACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ var express_1 = require("./middleware/express");
6
6
  Object.defineProperty(exports, "sentinel", { enumerable: true, get: function () { return express_1.sentinel; } });
7
7
  var fastify_1 = require("./middleware/fastify");
8
8
  Object.defineProperty(exports, "sentinelFastify", { enumerable: true, get: function () { return fastify_1.sentinelFastify; } });
9
- var next_1 = require("./middleware/next");
10
- Object.defineProperty(exports, "sentinelEdge", { enumerable: true, get: function () { return next_1.sentinelEdge; } });
9
+ var adapter_1 = require("./edge/adapter");
10
+ Object.defineProperty(exports, "sentinelEdge", { enumerable: true, get: function () { return adapter_1.sentinelEdge; } });
11
11
  var hono_1 = require("./middleware/hono");
12
12
  Object.defineProperty(exports, "sentinelHono", { enumerable: true, get: function () { return hono_1.sentinelHono; } });
13
13
  // Export client
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-turnstile",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "CAPTCHA-free API bot protection and abuse prevention middleware for Node.js, Express, Next.js, and serverless APIs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,17 +16,17 @@
16
16
  "keywords": [
17
17
  "api security",
18
18
  "api bot protection",
19
- "api abuse prevention",
20
19
  "anti bot",
21
- "bot protection",
22
20
  "captcha free",
23
21
  "cloudflare turnstile alternative",
22
+ "api abuse prevention",
24
23
  "credential stuffing",
25
- "signup fraud",
26
- "rate limiting alternative",
27
- "express middleware",
28
- "nextjs api",
29
- "serverless security"
24
+ "anti bot middleware",
25
+ "nodejs security",
26
+ "waf alternative",
27
+ "bot detection",
28
+ "signup protection",
29
+ "scraping prevention"
30
30
  ],
31
31
  "author": "Sentinel Security",
32
32
  "license": "MIT",
@@ -64,4 +64,4 @@
64
64
  "engines": {
65
65
  "node": ">=18.0.0"
66
66
  }
67
- }
67
+ }