Skip to content

3289david/silentshield

Repository files navigation

?썳截?SilentShield

Invisible bot protection ??no CAPTCHAs, no friction, no puzzles. Free forever.

Stop bots silently using 12 layers of behavioral analysis, browser fingerprinting, biometrics, and Proof-of-Work. Users never see a CAPTCHA or challenge. Open source, MIT licensed.

npm License: MIT jsDelivr

Website: https://sh.krl.kr
API: https://sh.krl.kr/api
CDN: https://cdn.jsdelivr.net/npm/silentshield@latest/dist/silentshield.min.js
GitHub: https://github.com/3289david/silentshield


Why SilentShield?

SilentShield reCAPTCHA v3 hCaptcha Cloudflare Turnstile
Free ??Unlimited 1M req/mo 1M req/mo Free tier only
No API key ?? ?? ?? ??
No CAPTCHAs ?? ?? ?? ??
Open source ??MIT ?? ?? ??
Self-hostable ?? ?? ?? ??
GDPR-safe ?? ?? ?? ??
Keyboard biometrics ?? ?? ?? ??
Font enumeration ?? ?? ?? ??
Bot learning ??every 10min Google ML Proprietary Proprietary

Quick Start (30 seconds)

Option A ??CDN (no build step)

<!-- 1. Add to <head> -->
<script src="https://cdn.jsdelivr.net/npm/silentshield@latest/dist/silentshield.min.js"></script>

<!-- 2. Tag your form -->
<form data-silentshield action="/submit" method="POST">
  <input type="text" name="name" />
  <input type="email" name="email" />
  <button type="submit">Send</button>
</form>

<!-- 3. Done. Token is auto-injected as _ss_token hidden input on submit. -->

No API key needed. No registration. It just works.

Option B ??npm

npm install silentshield
import SilentShield from 'silentshield';

const shield = new SilentShield({
  apiUrl: 'https://sh.krl.kr',  // or your self-hosted URL
});

shield.protect('#my-form');

Step 3 ??Verify on your server

// Node.js
const res = await fetch('https://sh.krl.kr/api/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ token: req.body._ss_token }),
});
const { valid, score, verdict } = await res.json();
// { valid: true, score: 91, verdict: 'human' }
if (!valid) return res.status(403).send('Blocked');
// PHP
$data = json_decode(file_get_contents('https://sh.krl.kr/api/verify', false,
  stream_context_create(['http' => [
    'method' => 'POST',
    'header' => 'Content-Type: application/json',
    'content' => json_encode(['token' => $_POST['_ss_token']])
  ]])
), true);
if (!$data['valid']) { http_response_code(403); exit; }
# Python
import requests
r = requests.post('https://sh.krl.kr/api/verify', json={'token': token})
if not r.json().get('valid'): abort(403)

Detection Layers (12 total)

Layer 1 ??Proof of Work (PoW)

Score weight: +15 / -35

The SDK starts solving a SHA-256 hashcash puzzle in the background the moment the page loads:

find nonce where SHA256(challenge + nonce).startsWith("000")

Real browsers solve it in ~100??00ms using crypto.subtle.digest(). The server verifies the solution on every submission. A missing or invalid PoW deducts 35 points. A pre-computed answer solved in <5ms deducts 25 points.


Layer 2 ??Behavioral Analysis

Score weight: up to +40

Tracks raw interaction counts:

Signal Human Bot
Mouse events >20 0
Scroll events >2 0
Click events >0 0
Double-click Occasional Never
Right-click (contextmenu) Occasional Never
Mouse down events Matches clicks 0 or mismatched
Tab/blur events Occasional Never

Zero interaction on all signals deducts 30 points.


Layer 3 ??Mouse Path Entropy

Score weight: +12 / -18

Measures the angle-change variance across every recorded mouse position. Humans move the mouse in natural, organic curves with random micro-tremors. The variance is typically >0.01 radians/event.

Bots either produce no mouse movement or move in perfectly straight lines (near-zero variance). Linear paths where mouseEntropy < 0.0001 with >10 events deducts 18 points.


Layer 4 ??Mouse Speed Variance

Score weight: +5

Calculates speed (pixels/ms) between consecutive mouse positions and measures its variance. Humans accelerate and decelerate constantly. Automated mouse injection tools typically move at constant speed, producing near-zero speed variance.


Layer 5 ??Keyboard Biometrics

Score weight: +18 / -45

Captures inter-keystroke timing intervals (up to 120 stored). The variance of these intervals reveals:

Variance Interpretation
>5000 Very human-like typing
>2000 Human
>500 Slightly irregular but plausible
<50 with >5 keys Robot typing (metronomic)
<10 with >3 keys Definitely scripted

Also scores: backspace/delete usage (+10, humans make typos), tab navigation (+5), excessive pasting (-8).

Form fill time scoring:

  • <300ms total fill time: -55 points (programmatic)
  • <800ms: -35 points
  • 2000ms real fill: +20 points


Layer 6 ??Canvas Fingerprinting

Score weight: +10 / -25

Renders a multi-layer canvas (gradient, two fonts, arc, bezier) and reads the last 80 bytes of the PNG data. Headless Chromium, PhantomJS, and server-side renderers produce characteristic patterns due to missing GPU acceleration and font rendering differences.

Known headless fingerprint patterns:

  • AAAAAAAAAA== suffix (empty/flat render)
  • wAAAANSUhEUg (SwiftShader artifact)

Layer 7 ??WebGL Renderer Detection

Score weight: +12 / -35

Calls WEBGL_debug_renderer_info to read the GPU vendor and renderer strings. Known software renderers used by headless environments:

Renderer string Verdict
SwiftShader Bot (-35)
llvmpipe Bot (-35)
Mesa Bot (-35)
ANGLE (software) Bot (-35)
VMware SVGA Bot (-35)
Real GPU (NVIDIA, AMD, Apple M) Human (+12)

Also reads MAX_TEXTURE_SIZE and MAX_VERTEX_ATTRIBS for additional GPU fingerprinting.


Layer 8 ??Audio Fingerprinting

Score weight: +8 / -8

Creates an AudioContext, routes a triangle oscillator through an AnalyserNode, and captures the frequency data signature (50-point FFT). Headless environments lack a real audio pipeline ??they either throw errors or produce flat zero output.


Layer 9 ??Font Enumeration

Score weight: +10 / -8

Renders a test string in 28 known fonts and measures the pixel width using canvas. A font is "detected" when its rendered width differs from the monospace baseline. Real desktop browsers have 10??5 fonts installed. Headless/sandboxed environments typically have 0??.


Layer 10 ??Environment & API Flags

Score weight: up to +45 / -70

Checks 20+ browser environment signals:

Signal Real Browser Headless
navigator.webdriver false true ??-70
Plugin count >3 0
window.chrome defined ??in Chrome Often absent
localStorage available ?? Sometimes blocked
indexedDB available ?? Sometimes blocked
speechSynthesis.getVoices() >5 voices 0
navigator.getBattery ?? Absent
window.innerWidth / screen.width ratio 0.4??.99 ratio ??.99
navigator.hardwareConcurrency >1 Often 1
pointer: fine media query ??on desktop Absent
hover: hover media query ??on desktop Absent
colorDepth ??24 ?? Sometimes 8
Cloud/datacenter IP Unusual Common
pixelRatio > 1 HiDPI device Often 1

Layer 11 ??Speech Synthesis

Score weight: +10 / -5

speechSynthesis.getVoices() returns the list of TTS voices installed on the OS. Real desktop browsers return 5??0 voices. Headless Chrome on a bare VPS returns 0.


Layer 12 ??Rotating Smart Honeypot

Score weight: -100 if triggered

Injects a CSS-invisible input field with one of 25 rotating names (website, homepage, company_url, phone2, etc.). The name rotates on every page load to defeat bot databases that learn to skip known honeypot names. Real users never fill it. Any value in the field = automatic bot verdict.


Bonus: Spam Content Filter

Score weight: -50 to -80

Analyzes submitted form text against 25+ spam keyword patterns (crypto, pharma, SEO, gambling, etc.). Multiple keyword hits compound the penalty.


Bonus: User Agent Matching

Score weight: -50 for known bots, +10 for real browsers

Checks against 23+ known bot/scraper user agents (curl, wget, Python requests, Selenium, Puppeteer, etc.) plus learned patterns from the bot learning system.


Bonus: Bot Learning (every 10 minutes)

A background node-cron job runs every 10 minutes and analyzes recent submissions to:

  • Learn new UA patterns from bot-scored traffic
  • Identify timing patterns unique to known bots
  • Update confidence scores on known bot signatures
  • Auto-expire stale patterns (>7 days without hits)

SDK Configuration

const shield = new SilentShield({
  // Optional: your domain's public key (for analytics only)
  publicKey: 'pk_abc123',

  // API endpoint (default: https://sh.krl.kr)
  apiUrl: 'https://your-own-server.com',

  // Score threshold below which to block (default: 45)
  // Score 0-100. Recommended: 45 (balanced) or 60 (strict)
  threshold: 45,

  // Show fake success to blocked bots (default: true)
  fakeSuccess: true,

  // Callback when a bot is detected
  onBot: (result) => console.log('Bot blocked', result.score),
});

// Protect a specific form
shield.protect('#contact-form');

// Or protect all [data-silentshield] forms
SilentShield.init({ apiUrl: 'https://sh.krl.kr' });

API Reference

POST /api/signal

Called automatically by the JS SDK. Accepts all behavioral signals and returns a one-time token.

Request: All signals collected by the SDK (see SDK source)

Response:

{
  "token": "550e8400-e29b-41d4-a716-446655440000",
  "score": 91,
  "verdict": "human"
}

Verdicts: "human" (??0) 쨌 "suspicious" (45??9) 쨌 "bot" (<45)


POST /api/verify

Your server calls this to validate a token before processing a form submission.

Request:

{ "token": "550e8400-e29b-41d4-a716-446655440000" }

Response:

{
  "valid": true,
  "score": 91,
  "verdict": "human",
  "timestamp": "2025-01-01T00:00:00.000Z"
}

Tokens are one-time use ??calling verify twice on the same token returns valid: false.


POST /api/register (optional)

Register your domain to get analytics keys. Completely optional.

Request:

{ "domain": "mysite.com", "name": "My Project" }

Response:

{
  "public_key": "pk_abc...",
  "secret_key": "sk_xyz...",
  "message": "Site registered ??dashboard analytics enabled"
}

GET /api/analytics/overview

Requires header: x-secret-key: sk_...

Returns 7-day traffic stats, bot rate, detection breakdown by layer.


Self-Hosting

One-command deploy (Ubuntu 22.04)

curl -fsSL https://raw.githubusercontent.com/3289david/silentshield/main/deploy.sh | bash

This script:

  1. Installs Node.js 20 via NodeSource
  2. Installs PM2 process manager
  3. Clones the repo to /opt/silentshield
  4. Builds the SDK (node build.js)
  5. Installs server dependencies
  6. Creates a .env with a random ADMIN_SECRET
  7. Starts the server with PM2 and saves the process list

Add SSL with nginx

apt install nginx certbot python3-certbot-nginx
certbot --nginx -d sh.krl.kr
cp nginx.conf /etc/nginx/sites-available/silentshield
ln -s /etc/nginx/sites-available/silentshield /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Environment variables

Variable Default Description
PORT 3000 Server port
DB_PATH ./silentshield.db SQLite database path
ADMIN_SECRET (random) Dashboard admin password
CORS_ORIGINS * Allowed CORS origins
RATE_LIMIT_MAX 200 Requests per window
RATE_LIMIT_WINDOW_MS 60000 Rate limit window (ms)
NODE_ENV development Set to production on VPS

GitHub Actions CI/CD

Set these secrets in your repo ??Settings ??Secrets:

Secret Value
VPS_HOST Your VPS IP address
VPS_USER SSH username (usually root)
VPS_SSH_KEY Private SSH key (RSA/ED25519)
NPM_TOKEN npm automation token
  • Push to main ??auto-deploys to VPS via SSH
  • Tag vX.Y.Z ??auto-publishes to npm

License

MIT ??free to use, modify, self-host, and redistribute.


Support

If SilentShield saves you from paying for reCAPTCHA Enterprise or hCaptcha, consider buying a coffee:

??buymeacoffee.com/rukkitofficial

About

Invisible bot protection - no CAPTCHAs, no friction.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors