Live Demo: https://savannaguard.com
SavannaGuard is a true self-hosted, privacy-respecting security layer designed to do exactly what traditional CAPTCHAs miss. While other solutions focus only on frustrating image puzzles and tracking users across the web, SavannaGuard functions as an invisible, supplementary defense-in-depth tool.
By combining Adaptive Proof-of-Work that auto-adjusts to attack volume, Per-Site ML Learning (Online Gaussian model) that learns your real users' behavior, Bot Signature Tracking that flags recurring attackers, JS Engine Timing Oracle that detects headless browsers via V8 JIT patterns, and Federated Bot Intelligence that shares threat intelligence across instances — it creates a robust shield that can act as an alternative to—or an additional layer alongside—existing security measures.
Zero third-party dependencies: No Redis requirement, no external API calls to big tech, and absolutely no behavioral data leaving your servers.
Automatically adjusts difficulty based on detected bot ratio. Under attack → harder puzzles. Quiet periods → minimal friction.
Online Gaussian model learns your legitimate users' behavior patterns after just 10+ human samples. Adapts to your traffic, not generic models.
Flags recurring attackers by IP+UA hash. Three matches = high confidence bot. Signature retention: 30 days.
Detects headless browsers (Puppeteer, Playwright, Selenium) via timing analysis of V8 JIT compilation patterns, crypto operations, and requestAnimationFrame behavior — without UA sniffing.
Human hands exhibit involuntary tremor at 8–12 Hz — a neurological constant present in all healthy users. SavannaGuard applies FFT to mouse velocity streams and measures power in this frequency band. Bots produce either flat noise or programmatic smoothness; neither matches biological entropy.
Before any form interaction, SavannaGuard passively collects WebRTC ICE candidates — the browser's own network self-report. Datacenter environments, single-interface VMs, and VPN leaks produce topology signatures that real home/office users never exhibit. Zero network requests required.
Self-hosted instances share bot signatures peer-to-peer via gossip protocol. When one instance detects a bot, all instances learn. No central server, no raw data leaves your network.
Mouse dynamics, keystroke cadence, canvas fingerprint, WebGL rendering, screen metrics, navigator properties, network timing, page timing, physiological tremor, WebRTC topology — all processed locally with scores 0-100.
- Website: https://savannaguard.com
- Repository: https://github.com/etherman-os/SavannaGuard
git clone https://github.com/etherman-os/SavannaGuard && cd SavannaGuard
cp .env.example .env
# Edit .env — at minimum, change SECRET_KEY and ADMIN_PASSWORD
nano .env
docker compose up -dOpen http://localhost:3000/admin and log in with your ADMIN_PASSWORD.
Generates a random SECRET_KEY and starts immediately:
git clone https://github.com/etherman-os/SavannaGuard && cd SavannaGuard && \
cp .env.example .env && \
sed -i "s/SECRET_KEY=change-me-to-a-random-string/SECRET_KEY=$(openssl rand -hex 32)/" .env && \
sed -i "s/ADMIN_PASSWORD=admin/ADMIN_PASSWORD=change-me-before-deploying/" .env && \
docker compose up -d
⚠️ Replacechange-me-before-deployingwith a strong password in the command above.
git clone https://github.com/etherman-os/SavannaGuard && cd SavannaGuard
pnpm install
pnpm build
cp .env.example .env
# Edit .env — set SECRET_KEY and ADMIN_PASSWORD
pnpm --filter @savannaguard/server start| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
✅ Yes | — | Token encryption key. Generate with openssl rand -hex 32. |
ADMIN_PASSWORD |
No | admin |
Admin dashboard login password. |
PORT |
No | 3000 |
HTTP server port. |
DB_PATH |
No | ./data/savannaguard.db |
SQLite database path. Use /data/ prefix in Docker. |
LOG_LEVEL |
No | info |
Logging verbosity: debug, info, warn, error. |
FEDERATION_ENABLED |
No | false |
Enable P2P federation for sharing bot signatures. |
FEDERATION_PEERS |
No | — | Comma-separated peer URLs (e.g. https://sg1.example.com,https://sg2.example.com). |
FEDERATION_PSK |
No | — | Pre-shared key for peer authentication. Required if federation is enabled. |
FEDERATION_SYNC_INTERVAL |
No | 300000 |
Active peer sync interval in ms (5 min). |
FEDERATION_OFFLINE_SYNC_INTERVAL |
No | 1800000 |
Offline peer recovery interval in ms (30 min). |
FEDERATION_OFFLINE_THRESHOLD |
No | 3 |
Consecutive failures before marking peer offline. |
FEDERATION_MAX_PAYLOAD_BYTES |
No | 5242880 |
Max sync payload size in bytes (5 MB). |
FEDERATION_REQUEST_TIMEOUT_MS |
No | 30000 |
Peer request timeout in ms. |
FEDERATION_MAX_RETRIES |
No | 3 |
Max retry attempts for failed peer requests. |
FEDERATION_BASE_RETRY_DELAY_MS |
No | 5000 |
Base delay between retries in ms. |
FEDERATION_MAX_RETRY_DELAY_MS |
No | 60000 |
Maximum delay between retries in ms. |
The server exposes a /health endpoint that returns {"ok": true}. Docker Compose uses this for automatic container health checks.
To enable P2P bot intelligence sharing between SavannaGuard instances:
- Set
FEDERATION_ENABLED=trueon both instances - Set the same
FEDERATION_PSKon both instances - Add each instance's URL to the other's
FEDERATION_PEERS - Restart both instances
See FEDERATION.md for the full protocol specification.
SavannaGuard usage flow in production:
- Frontend requests a challenge from
/api/v1/challenge/create. - Widget solves PoW and sends telemetry to
/api/v1/challenge/solve. - Backend validates the issued token with
/api/v1/token/validatebefore processing user action.
pnpm testTo run only server tests:
pnpm --filter @savannaguard/server test<!-- Add the widget script -->
<script src="http://localhost:3000/widget/savanna-widget.iife.js" async></script>
<!-- Hidden token field in your form -->
<form id="my-form">
<input type="hidden" name="savanna_token" id="savanna_token">
<input type="email" name="email" placeholder="Email">
<button type="submit">Submit</button>
</form>
<script>
// Optional if you want to override API URL manually.
// Widget auto-detects API origin from script src by default.
window.SavannaGuard.init('http://localhost:3000');
document.getElementById('my-form').addEventListener('submit', async (e) => {
e.preventDefault();
const token = await window.SavannaGuard.getToken();
if (token) {
document.getElementById('savanna_token').value = token;
}
e.target.submit();
});
</script>- Server: Fastify + SQLite — handles PoW challenges, token generation, session scoring
- Widget: Vanilla TS via Vite — Web Worker PoW solver, behavioral collectors (6.6kb gzip)
- Admin: Alpine.js UI served by server — stats, flagged sessions, settings
SavannaGuard's detection methods are grounded in observable biological and network phenomena that bots cannot easily replicate.
Human hands exhibit involuntary tremor at 8–12 Hz — a neurological constant present in all healthy users. SavannaGuard applies FFT to mouse velocity streams and measures power in this frequency band. Bots produce either flat noise or programmatic smoothness; neither matches biological entropy.
Before any form interaction, SavannaGuard passively collects WebRTC ICE candidates — the browser's own network self-report. Datacenter environments, single-interface VMs, and VPN leaks produce topology signatures that real home/office users never exhibit. Zero network requests required.
We invite security researchers to attempt bypass. Open an issue with your approach — successful bypasses that get patched earn credit in this README.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/challenge/create | Create PoW challenge |
| POST | /api/v1/challenge/solve | Submit PoW solution + behavioral data |
| POST | /api/v1/token/validate | Validate a token |
| GET | /health | Health check (returns {"ok":true}) |
| GET | /widget/savanna-widget.iife.js | Widget JavaScript bundle |
| Method | Path | Description |
|---|---|---|
| GET | /admin | Admin dashboard (stats page) |
| GET | /admin/threat | Threat intelligence tab |
| GET | /admin/flagged | Flagged bot sessions |
| GET | /admin/settings | Settings page |
| GET | /admin/federation | Federation peer management |
| GET | /admin/vpn | VPN Detection (upsell) |
| GET | /admin/mobile | Mobile SDK (upsell) |
| GET | /admin/multi-tenant | Multi-Tenant (upsell) |
| Method | Path | Description |
|---|---|---|
| GET | /admin/api/stats | Dashboard statistics |
| GET | /admin/api/stats/timeseries | Time-series chart data |
| GET | /admin/api/threat | Threat intelligence data |
| GET | /admin/api/learning | ML learning data |
| GET | /admin/api/signatures | Bot signature database |
| GET | /admin/api/flagged | Flagged session list |
| GET | /admin/api/settings | Current settings |
| POST | /admin/api/settings | Update settings |
| GET | /admin/api/passive-protection | Passive protection stats |
| GET | /admin/api/federation/peers | List federation peers |
| POST | /admin/api/federation/peers | Add federation peer |
| DELETE | /admin/api/federation/peers/:peerId | Remove federation peer |
| POST | /admin/api/federation/sync | Trigger federation sync |
| GET | /admin/api/federation/stats | Federation statistics |
| Method | Path | Description |
|---|---|---|
| GET | /federation/state | Get bot signature state |
| POST | /federation/state | Receive signature state (pull response) |
| POST | /federation/push | Push new signatures to peer |
| POST | /federation/sync | Pull signatures from peer |
| Feature | SavannaGuard | Cloudflare Turnstile | hCaptcha |
|---|---|---|---|
| Self-hosted | ✅ | ❌ | ❌ |
| No external API calls | ✅ | ❌ | ❌ |
| ML learning per-site | ✅ | ❌ | ✅ |
| Headless browser detection | ✅ | Limited | ❌ |
| Physiological tremor analysis | ✅ | ❌ | ❌ |
| WebRTC topology oracle | ✅ | ❌ | ❌ |
| Federated threat intelligence | ✅ | ❌ | ❌ |
| Adaptive PoW difficulty | ✅ | ❌ | ❌ |
| Behavioral signal collectors | 10 signals | Unknown | Minimal |
| SQLite (no Redis) | ✅ | N/A | N/A |
Fastify · SQLite · TypeScript · Vite · Alpine.js · Docker Compose
- This repository is source-available (not OSI open source).
- Community self-hosting is allowed.
- Hosted/managed resale requires a separate commercial agreement.
- See LICENSE and COMMERCIAL_USE.md for details.
- This repo: Community verification core (self-hostable).
- Paid/Private repo: billing, tenant provisioning, managed control-plane, enterprise features.
- No Redis — challenge/session state uses SQLite only
- No behavioral raw data — only aggregate scores (0–100) stored
- IP addresses hashed (SHA256 + rotating daily salt), never stored in plain text
import fetch from 'node-fetch';
async function verifySavannaToken(token: string) {
const response = await fetch('http://localhost:3000/api/v1/token/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
const data = await response.json();
// { valid: true, verdict: 'human', score: 84 }
return data;
}