Browser SDK for Sentinel fraud detection and device fingerprinting
npm install @sentineld/browserBrowser SDK for Sentinel fraud detection and device fingerprinting. Identify visitors, detect bots, and assess fraud risk in real-time.
``bash`
npm install @sentinel-sdk/browser
Or via CDN:
`html`
`typescript
import Sentinel from '@sentinel-sdk/browser';
// Initialize with your API key
Sentinel.init({
endpoint: 'https://sentinel.yourdomain.com/api',
apiKey: 'pk_live_xxxxxxxxxxxxx',
});
// Identify the current visitor
const result = await Sentinel.identify();
console.log(result.visitor_id); // Unique visitor ID
console.log(result.is_new); // First time seeing this visitor
// Send visitor_id to your backend -- fetch risk scores
// server-side using the sk_live_* secret key API
`
`html`
`typescript`
Sentinel.init({
endpoint: string, // Your Sentinel API endpoint
apiKey: string, // Your public API key (pk_live_xxx)
});
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| endpoint | string | Yes | Your Sentinel API base URL |apiKey
| | string | Yes | Public API key (pk_live_ prefix). Used for request signing, encryption, and Proof-of-Work authentication. |
Initialize the SDK. Must be called before other methods.
Calling init() eagerly starts background work so that identify() is fast:
- Fingerprint collection (canvas, WebGL, audio, fonts)
- WebRTC network signal gathering
- WASM crypto module loading (signing, encryption, PoW solver)
- Anti-debug monitoring
Identify the current visitor and get risk assessment.
A single call handles the full pipeline: collecting signals, fetching and solving a Proof-of-Work challenge, encrypting and signing the payload, and sending it to the server. No separate setup steps are needed.
`typescript
const result = await Sentinel.identify({
sessionId: 'optional-session-id', // Auto-generated if not provided
accountId: 'user-123', // Optional: link to your user ID
});
// Returns:
{
visitor_id: string, // Stable visitor identifier
confidence: number, // Fingerprint confidence (0-1)
method: string, // Resolution method ("fingerprint")
is_new: boolean, // First time seeing this visitor
visit_count: number, // Total visits from this device
}
// Risk and automation scores are NOT returned to the browser.
// Fetch them server-side using your sk_live_* key:
// GET /api/v1/visitors/{visitor_id}/events
`
Track a custom event (e.g., login, purchase, signup).
`typescript`
const result = await Sentinel.track({
type: 'login', // Event type
accountId: 'user-123', // Optional: your user ID
metadata: { // Optional: custom data
method: 'password',
success: true,
},
});
Get the current visitor's fingerprint hash without making an API call.
`typescript`
const visitorId = await Sentinel.getVisitorId();
Detect if the browser is in incognito/private mode.
`typescript`
const isIncognito = await Sentinel.detectIncognito();
When you call identify() or track(), the SDK performs these steps in parallel for minimal latency:
1. Collect signals — fingerprint (canvas, WebGL, audio, fonts), network (WebRTC), behavior (mouse/keyboard patterns), integrity (headless/stealth/VM/extension detection), and validation signals (UA, screen, timezone, languages).
2. Fetch & solve Proof-of-Work — requests a challenge from GET /v1/challenge and solves it using a native WASM SHA-256 loop. The solution is attached as X-Pow-Id and X-Pow-Nonce headers. This runs concurrently with signal collection so it adds zero extra latency.
3. Encrypt & sign — the payload is AES-GCM encrypted and HMAC-SHA256 signed using the pk_live_ API key. Both operations use the WASM crypto module when available.
4. POST — sends the encrypted, signed payload with PoW headers. The server decrypts, verifies the signature, validates the PoW solution, cross-references HTTP headers against SDK-reported values, and returns the risk assessment.
``
init() identify()
│ │
├─ Load WASM crypto ├─ Collect signals ──────┐
├─ Start fingerprinting ├─ Fetch PoW challenge ──┤ (parallel)
├─ Start network probes │ │
├─ Start anti-debug │ Solve PoW (WASM) ─────┘
│ │
│ ├─ Sign + Encrypt payload
│ ├─ POST /v1/identify
│ └─ Return risk result
Every identify() and track() call requires solving a SHA-256 Proof-of-Work challenge before the server accepts it. This raises the cost of automation — a bot farm must burn real CPU per request.
- Default difficulty: 16 leading zero bits (~65K SHA-256 hashes, ~30-50ms)
- Adaptive difficulty: repeated failures from the same IP increase difficulty (up to +8 bits, making it 256x harder)
- The solver runs inside a compiled WASM binary with integrity verification — there is no JavaScript fallback to reverse-engineer
All request bodies are AES-GCM encrypted and HMAC-SHA256 signed. The server decrypts and verifies signatures before processing. Replay attacks are prevented by unique nonces and timestamps.
The server cross-references actual HTTP headers against SDK-reported values:
| Check | What it catches |
|-------|----------------|
| User-Agent mismatch | curl with spoofed payload |
| Accept-Language mismatch | Forged language signals |
| Sec-CH-UA mismatch | Wrong browser family claims |
| Missing Sec-Fetch-Site | Non-browser HTTP clients |
| Missing Sec-CH-UA | Automation tools omitting client hints |
| Connection: keep-alive | HTTP/1.1 libraries pretending to be browsers |
The SDK detects and deters reverse engineering:
- Passive detection — timing API integrity checks, Image.id getter traps for DevTools, native function code-length monitoring, requestAnimationFrame cadence analysis, stack depth anomaly detection
- Active deterrence — debugger statement bombing only activates after high-confidence detection (score-based system with threshold)
- Anti-tampering — self-defending function wrappers, environment verification, code integrity checks, prototype modification detection
- Stealth detection — catches puppeteer-stealth, playwright-stealth, and similar plugins through Function.toString anomalies, iframe isolation checks, Chrome runtime inconsistencies, and Proxy wrapper detection
Signals collected and scored server-side:
- WebDriver / navigator.webdriver flag
- Headless browser indicators (missing plugins, languages, WebGL, Chrome runtime)
- CDP markers (Puppeteer, Playwright, Selenium, ChromeDriver)
- VM detection (VMware, VirtualBox, Parallels, QEMU)
- Canvas and WebGL manipulation detection
- Privacy/automation extension detection
- VPN, proxy, and datacenter IP detection (MaxMind GeoIP + datacenter CIDR lists)
| Score Range | Risk Level | Description |
|-------------|------------|-------------|
| 0-20 | Low | Likely legitimate user |
| 21-50 | Medium | Some suspicious signals |
| 51-80 | High | Likely fraudulent |
| 81-100 | Critical | Strong fraud indicators |
| Code | Description |
|------|-------------|
| BOT_WEBDRIVER | WebDriver/automation detected |BOT_HEADLESS
| | Headless browser detected |BOT_CDP
| | Chrome DevTools Protocol automation |BOT_STEALTH
| | Stealth plugin detected |VPN_DETECTED
| | VPN/proxy detected |DATACENTER_IP
| | IP from datacenter range |TZ_MISMATCH
| | Timezone doesn't match IP location |INCOGNITO
| | Private browsing mode |HTTP_UA_MISMATCH
| | HTTP User-Agent doesn't match SDK-reported UA |MISSING_CLIENT_HINTS
| | Browser should send Sec-CH-UA but it's missing |MISSING_SEC_FETCH
| | Browser should send Sec-Fetch-Site but it's missing |SIGNAL_INCONSISTENCY
| | Multiple signal cross-validation failures |
The Sentinel API server is configured via environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| SENTINEL_PORT | 8080 | API server port |SENTINEL_POSTGRES_DSN
| | — | PostgreSQL connection string (required) |SENTINEL_REDIS_ADDR
| | localhost:6379 | Redis address |SENTINEL_HMAC_SECRET
| | — | Server-side HMAC secret (required) |SENTINEL_ALLOWED_ORIGINS
| | localhost | CORS allowed origins |SENTINEL_POW_ENABLED
| | true | Enable Proof-of-Work challenges |SENTINEL_POW_DIFFICULTY
| | 16 | PoW difficulty (leading zero bits) |SENTINEL_REQUIRE_API_KEY
| | false | Require API keys on SDK routes |SENTINEL_MAXMIND_CITY_PATH
| | — | Path to GeoLite2-City.mmdb |SENTINEL_MAXMIND_ASN_PATH
| | — | Path to GeoLite2-ASN.mmdb |SENTINEL_ENABLE_PROXY_DETECTION
| | true | Enable VPN/proxy risk scoring |
Full TypeScript support is included. Import types:
`typescript``
import Sentinel, {
SentinelConfig,
IdentifyResponse,
EventResponse,
} from '@sentinel-sdk/browser';
Requires WebAssembly support (all modern browsers):
- Chrome 57+
- Firefox 52+
- Safari 11+
- Edge 16+
MIT