The Three-Actor Model
Most bot detection systems classify sessions as either human or bot. That model is outdated. Nyasa introduces a third actor: AuthorizedAgent. This is an AI agent acting legitimately on behalf of a real user—like a shopping assistant or fintech integration. Blocking these agents means turning away real business.
Nyasa classifies every session as exactly one of:
- Human: no detection rules fired
- AuthorizedAgent: holds a valid cryptographic identity claim, automatically bypasses bot rules
- UnauthorizedBot: one or more detection rules fired
The AuthorizedAgent category is the real addition. An AI shopping assistant should not have to pass a CAPTCHA. It presents a signed identity claim, and Nyasa recognizes it.
The Signal Stack: 24 Signals Across Three Layers
Behavioral Signals (13)
| Signal | What it measures |
|---|---|
| Keystroke dwell and flight time | How long keys are held, time between keystrokes |
| Mouse path curvature | Deviation from straight-line movement |
| Paste vs typed ratio | Whether text was typed character by character or bulk-pasted |
| Click precision (center offset) | Distance from click point to element center |
| Session burst-pause rhythm | Alternation between fast activity and idle gaps |
| Backspace corrections | Correction frequency during text input |
| Scroll depth | How far down the page a session goes |
| Touch mechanics | Multi-touch patterns and pressure distribution |
| Field-level timing | Time spent on each form field before moving on |
| Input origin | Typed vs pasted vs dropped vs programmatic fill |
| Tab visibility | Whether the session loses and regains focus |
| File upload mechanics | How files are attached (drag, click, or programmatic) |
| Session rhythm | Overall pace and structure of the session |
Fingerprint Signals (8)
- Webdriver and CDP markers
- Iframe vs parent plugin consistency
- Canvas fingerprint hash
- WebGL renderer string (SwiftShader and LLVMpipe detection)
- Audio fingerprint via OfflineAudioContext
- Incognito detection via storage quota probe
- Timezone vs navigator.language consistency
- Persistent device UUID with isNew flag
Network Signals (3)
- Page reaction time (time from page load to first interaction)
- Connection type (from Navigator API)
- Page load timing (Performance timing breakdown)
Detection Rules: Six Rules That Fire Independently
- isHeadless: Reads fingerprint layer for automation markers: webdriver properties, CDP exposure, WebGL renderer strings like SwiftShader or LLVMpipe, iframe/parent inconsistencies.
- isScripted: Reads behavioral signals for bot-like input patterns: fields filled in milliseconds, no backspace, perfect field sequence.
- isLLMAgent: The hardest rule. Seven signals evaluated together (see below).
- isAuthorizedAgent: Reads
window.__nyasaAgentSignatureor a meta tag. If valid, session is AuthorizedAgent and no other rules run. - isUploadAutomation: Checks file upload mechanics. Human uploads use file picker or drag; programmatic uploads bypass both.
- isMultimodalBot: Looks for cross-signal contradictions. Reads sibling DetectionResults to catch near-miss compositions.
isLLMAgent Deep Dive
LLM agents are genuinely hard to distinguish from fast, focused humans. Seven signals are evaluated together:
- Machine-speed keystroke bursts under 20ms: Human dwell times cluster around 80-200ms.
- Mouse stillness above 70%: Humans move the mouse constantly; LLM-driven sessions often keep the cursor parked.
- Uniform keystroke variance near zero: Human typing has natural rhythm variation; LLM agent keystrokes have suspiciously consistent intervals.
- Zero backspace rate: Humans make corrections. An agent filling a form it computed upfront doesn't backspace.
- Pixel-perfect click precision: Humans click near the center but not exactly on it; agents click at the computed center coordinate.
- Missing field exploration: Humans often click into a field, leave, return, re-read the label. LLM agents visit each field once in sequence and move on.
- No idle micro-pauses: Humans have sub-second pauses between thoughts. Agent sessions show continuous forward progress.
No single signal is definitive. isLLMAgent requires several of these signals to align before it fires.
Feature Extraction Layer
Early versions had each detection rule computing its own derived metrics, causing duplicated math and divergence. The feature extraction layer runs once per session and computes 8 shared derived metrics before any detection rule evaluates. Every rule reads from the same computed values, ensuring consistency.
Verdict System
Every session gets a verdict object:
interface NyasaVerdict {
type: 'Human' | 'AuthorizedAgent' | 'UnauthorizedBot';
confidence: number; // 0.0 to 1.0
badges: DetectionBadge[]; // which rules fired or nearly fired
}
Confidence is a noisy-OR score across all active rules. If one rule fires with 0.8 confidence and a second fires with 0.6, the combined score is 1 - (1 - 0.8) * (1 - 0.6) = 0.92. Badge labels tell you which rules contributed.
The verdict payload ships via navigator.sendBeacon. Non-blocking, fires after the page interaction completes, survives page unload.
Architecture and Installation
The SDK runs entirely in the browser. Signals are collected passively as the session progresses. Feature extraction runs on a timer and on key events. Detection rules evaluate when a verdict is requested or automatically at session end.
Nyasa ships as both ESM and IIFE from a single tsup build config.
ESM for bundlers:
import { createNyasa } from '@devanshhq/nyasa';
const nyasa = createNyasa({
endpoint: 'https://your-backend.com/nyasa',
agentBypass: true,
});
nyasa.start();
IIFE for script tags:
Installation:
npm install @devanshhq/nyasa
Minimal setup:
import { createNyasa } from '@devanshhq/nyasa';
const nyasa = createNyasa({
endpoint: '/api/session-verdict',
});
nyasa.start();
const verdict = await nyasa.getVerdict();
console.log(verdict.type); // 'Human' | 'AuthorizedAgent' | 'UnauthorizedBot'
console.log(verdict.confidence); // 0.0 - 1.0
console.log(verdict.badges); // ['isHeadless', 'isLLMAgent', ...]
Agent Bypass
If you're building an AI agent that needs to interact with Nyasa-protected pages, set the signature before the SDK initializes:
window.__nyasaAgentSignature = {
token: 'signed-jwt-from-your-auth-server',
agentId: 'shopping-assistant-v2',
issuedAt: Date.now(),
};
Or via meta tag:
The isAuthorizedAgent rule reads this claim, validates the signature, and short-circuits to AuthorizedAgent.
What It Catches That Others Miss
Traditional fingerprinting misses LLM agents because they run in real browsers with patched automation markers. Traditional behavioral analytics miss them because modern LLM agents have realistic typing cadence. Nyasa catches them through the combination: machine-speed micro-bursts that no human produces, combined with zero backspace rate and pixel-perfect clicks. Any one signal has false positives. All three together don't.
Try It
- Live demo: nyasa.devanshtiwari.com
- GitHub: github.com/Devansh-365/nyasa
- npm:
npm install @devanshhq/nyasa


