{"slug": "i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works", "title": "I Built a Browser SDK That Detects LLM Agents. Here's How It Works.", "summary": "Nyasa, a browser SDK designed to detect three types of web sessions: human users, unauthorized bots, and authorized AI agents. Unlike traditional bot detection that blocks all automated traffic, Nyasa uses 24 signals across behavioral, fingerprint, and network layers to classify sessions, with a key feature being the ability to recognize and allow legitimate AI agents through signed identity claims. The system employs six independent detection rules, including a complex \"isLLMAgent\" rule that analyzes seven combined signals to distinguish AI agents from fast human users.", "body_md": "Every bot detection system I've seen works with two actors: human or bot. Block the bot, let the human through.\n\nThat model is wrong in 2026.\n\nThere is a third actor: AI agents acting legitimately on behalf of real users. Shopping assistants. Automated onboarding flows. Fintech integrations. These agents look like bots by every traditional signal: headless browser characteristics, scripted input patterns, no idle pauses. But blocking them means turning away real business.\n\nI built Nyasa to handle all three.\n\n## Why existing tools fail now\n\nCAPTCHA was built for bots that couldn't read distorted text. Those bots are dead. CAPTCHA farms solve challenges at $0.50 each. LLM vision models solve them in milliseconds.\n\nDevice fingerprinting catches webdriver flags and automation markers. Modern headless browsers patch those out. Playwright, Puppeteer, and every major automation framework have community patches specifically for passing fingerprint checks.\n\nBehavioral analytics (typing cadence, mouse movement) catch scripted bots. But LLM agents don't use scripted input anymore. They type at 60-80 WPM with realistic keystroke intervals, move the mouse in curved paths, and pause at form fields before filling them.\n\nThe deeper problem is architectural. Existing systems ask: \"Is this a bot?\" Nyasa asks: \"Who is this session?\" The answer is one of three things.\n\n## The three-actor model\n\nNyasa classifies every session as exactly one of:\n\n-\n**Human**: no detection rules fired -** AuthorizedAgent**: holds a valid cryptographic identity claim, automatically bypasses bot rules -** UnauthorizedBot**: one or more detection rules fired\n\nThe AuthorizedAgent category is the real addition. An AI shopping assistant built on top of your product shouldn't have to pass a CAPTCHA. It should present a signed identity claim, and your system should recognize and respect it. Nyasa handles that handshake.\n\n## The signal stack\n\n24 signals total, split across three layers.**Behavioral signals (13)**| Signal | What it measures |\n|---|---|\n| Keystroke dwell and flight time | How long keys are held, time between keystrokes |\n| Mouse path curvature | Deviation from straight-line movement |\n| Paste vs typed ratio | Whether text was typed character by character or bulk-pasted |\n| Click precision (center offset) | Distance from click point to element center |\n| Session burst-pause rhythm | Alternation between fast activity and idle gaps |\n| Backspace corrections | Correction frequency during text input |\n| Scroll depth | How far down the page a session goes |\n| Touch mechanics | Multi-touch patterns and pressure distribution |\n| Field-level timing | Time spent on each form field before moving on |\n| Input origin | Typed vs pasted vs dropped vs programmatic fill |\n| Tab visibility | Whether the session loses and regains focus |\n| File upload mechanics | How files are attached (drag, click, or programmatic) |\n| Session rhythm | Overall pace and structure of the session |**Fingerprint signals (8)**| Signal | What it catches |\n|---|---|\n| Webdriver and CDP markers | Automation framework artifacts in the browser object |\n| Iframe vs parent plugin consistency | Mismatches between iframe and parent context |\n| Canvas fingerprint hash | Rendering environment identifier |\n| WebGL renderer string | SwiftShader and LLVMpipe detection (headless GPU emulation) |\n| Audio fingerprint via OfflineAudioContext | Audio processing environment |\n| Incognito detection via storage quota probe | Private browsing mode |\n| Timezone vs navigator.language consistency | Region mismatch between system and browser config |\n| Persistent device UUID with isNew flag | Tracks whether this device has been seen before |**Network signals (3)**| Signal | What it measures |\n|---|---|\n| Page reaction time | Time from page load to first interaction |\n| Connection type | Network type reported by Navigator API |\n| Page load timing | Performance timing breakdown |\n\n## Detection rules\n\nSix rules run against the collected signals. Each rule fires independently. Any fired rule pushes the verdict toward UnauthorizedBot, except isAuthorizedAgent which short-circuits to AuthorizedAgent regardless of other rules.**isHeadless** reads the fingerprint layer for automation markers: webdriver properties, CDP exposure, WebGL renderer strings like SwiftShader or LLVMpipe, iframe/parent inconsistencies. If the browser environment looks like a headless runner, this fires.**isScripted** reads behavioral signals for bot-like input patterns. Scripted bots fill fields in milliseconds, never backspace, and move between fields in perfect sequence. This rule catches that pattern.**isLLMAgent** is the hardest one. I'll cover it in the next section.**isAuthorizedAgent** reads the agent identity claim from `window.__nyasaAgentSignature`\n\nor a meta tag. If a valid claim is present, the session is classified as AuthorizedAgent and no further rules are evaluated.**isUploadAutomation** checks file upload mechanics. Human uploads involve a file picker dialog or drag interaction. Programmatic uploads bypass both. This rule catches the bypass pattern.**isMultimodalBot** looks for cross-signal contradictions: a session that passes one rule but shows soft signals consistent with automation across several others. It reads sibling DetectionResults directly rather than re-sampling signals, which avoids divergence when two rules read the same underlying data at different times.\n\n## isLLMAgent deep dive\n\nThis is the rule I spent the most time on. LLM agents are genuinely hard to distinguish from fast, focused humans. Seven signals, evaluated together:\n\n-**Machine-speed keystroke bursts under 20ms.** Human dwell times cluster around 80-200ms. Sub-20ms bursts don't happen in human typing. -**Mouse stillness above 70%.** Humans move the mouse constantly, even when not clicking. 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 of an element 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.\n\nNo single signal is definitive. A fast typist has low keystroke variance. A focused user might not backspace. isLLMAgent requires several of these signals to align before it fires.\n\n## Feature extraction architecture\n\nEarly versions of Nyasa had each detection rule computing its own derived metrics from raw signals. That caused two problems: duplicated math across rules, and rules diverging when they read the same underlying signal at slightly different times during a session.\n\nThe feature extraction layer solves this. It runs once per session and computes 8 shared derived metrics before any detection rule evaluates. Every rule reads from the same computed values.\n\nThe metrics include things like overall typing variance, click precision distribution, and mouse activity ratio. Computing them once means a detection rule that needs \"mouse stillness percentage\" and a sibling rule that also needs it will always agree on the number.\n\nisMultimodalBot benefits from this the most. It reads the DetectionResults of its sibling rules rather than re-running signal evaluation. Near-miss composition (where a session nearly triggers multiple rules without fully triggering any) gets caught without any rule having to re-sample data that may have aged out.\n\n## The verdict system\n\nEvery session gets a verdict object with three fields:\n\n```\ninterface NyasaVerdict {\n  type: 'Human' | 'AuthorizedAgent' | 'UnauthorizedBot';\n  confidence: number;           // 0.0 to 1.0\n  badges: DetectionBadge[];     // which rules fired or nearly fired\n}\n```\n\nConfidence 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)`\n\n= 0.92. Multiple weak signals compound.\n\nBadge labels tell you which rules contributed. A session might come back as UnauthorizedBot with badges for `isHeadless`\n\nand `isLLMAgent`\n\n, which tells you this is a headless LLM agent. That gets different handling than a scripted form filler.\n\nThe verdict payload ships via `navigator.sendBeacon`\n\n. Non-blocking, fires after the page interaction completes, survives page unload. Your analytics pipeline or backend decision layer receives it without adding latency to the user-facing flow.\n\n## Architecture\n\nThe SDK runs entirely in the browser. Signals are collected passively as the session progresses. The feature extraction layer runs on a timer and on key events. Detection rules evaluate when a verdict is requested or automatically at session end.\n\n## Dual packaging\n\nNyasa ships as both ESM and IIFE from a single tsup build config.\n\nESM for bundlers:\n\n``` js\nimport { createNyasa } from '@devanshhq/nyasa';\n\nconst nyasa = createNyasa({\n  endpoint: 'https://your-backend.com/nyasa',\n  agentBypass: true,\n});\n\nnyasa.start();\n```\n\nIIFE for script tags, no bundler required:\n\n```\n<script src=\"https://cdn.jsdelivr.net/npm/@devanshhq/nyasa/dist/nyasa.iife.js\"></script>\n<script>\n  Nyasa.createNyasa({\n    endpoint: '/api/nyasa',\n    agentBypass: true,\n  }).start();\n</script>\n```\n\nSame source, two outputs. The tsup config handles the split. No separate build for CDN distribution.\n\n## Installation and quick start\n\n```\nnpm install @devanshhq/nyasa\n```\n\nMinimal setup:\n\n``` js\nimport { createNyasa } from '@devanshhq/nyasa';\n\nconst nyasa = createNyasa({\n  endpoint: '/api/session-verdict',\n});\n\nnyasa.start();\n\n// Get the verdict at any point\nconst verdict = await nyasa.getVerdict();\nconsole.log(verdict.type);       // 'Human' | 'AuthorizedAgent' | 'UnauthorizedBot'\nconsole.log(verdict.confidence); // 0.0 - 1.0\nconsole.log(verdict.badges);     // ['isHeadless', 'isLLMAgent', ...]\n```\n\nOn the backend, you receive the verdict via the beacon endpoint and decide what to do: allow, challenge, block, or route differently based on the type.\n\n## The authorized agent bypass\n\nIf you're building an AI agent that needs to interact with Nyasa-protected pages, set the signature before the SDK initializes:\n\n```\n// In your agent code, before navigating to the page\nwindow.__nyasaAgentSignature = {\n  token: 'signed-jwt-from-your-auth-server',\n  agentId: 'shopping-assistant-v2',\n  issuedAt: Date.now(),\n};\n```\n\nOr via meta tag for server-rendered flows:\n\n```\n<meta name=\"nyasa-agent-signature\" content=\"signed-jwt-here\" />\n```\n\nThe `isAuthorizedAgent`\n\nrule reads this claim, validates the signature, and short-circuits to `AuthorizedAgent`\n\n. No other rules run. The session is logged as a known agent, not blocked as a bot.\n\nThis is the part that most bot detection tools don't have a concept for. If you're running a fintech integration or an AI onboarding assistant, you shouldn't have to fight your own security layer.\n\n## What it catches that others miss\n\nTraditional 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.\n\nNyasa 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.\n\nThe multimodal rule catches the edge cases: sessions that pass fingerprinting and look almost human behaviorally but have soft contradictions across signals that don't fit either profile cleanly.\n\n**Live:**[nyasa.devanshtiwari.com](https://nyasa.devanshtiw", "url": "https://wpnews.pro/news/i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works", "canonical_source": "https://dev.to/devansh365/i-built-a-browser-sdk-that-detects-llm-agents-heres-how-it-works-3bdk", "published_at": "2026-05-22 04:19:55+00:00", "updated_at": "2026-05-22 04:31:14.724202+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning", "large-language-models", "cybersecurity", "developer-tools"], "entities": ["Nyasa", "Playwright", "Puppeteer", "CAPTCHA"], "alternates": {"html": "https://wpnews.pro/news/i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works", "markdown": "https://wpnews.pro/news/i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works.md", "text": "https://wpnews.pro/news/i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works.txt", "jsonld": "https://wpnews.pro/news/i-built-a-browser-sdk-that-detects-llm-agents-here-s-how-it-works.jsonld"}}