Detection catalog

Every check Kitsune runs, by layer. A convicting check — a cross-layer contradiction, an automation artifact, a forged native — can call a session a bot on its own. The rest only add weight.

200checks
153convicting
5layers

cross-layer 14

net.accept_encoding_vs_ua convictsUA claims a modern browser but Accept-Encoding omits Brotli (scripted client)
net.accept_lang_vs_navigator convictsHTTP Accept-Language contradicts the JS navigator.languages locale
net.ch_platform_header_vs_ua convictsHTTP Sec-CH-UA-Platform contradicts the JS UA platform
net.ch_ua_mobile_vs_ua convictsSec-CH-UA-Mobile form factor contradicts the UA's mobile-ness
net.ch_ua_version_vs_ua convictsSec-CH-UA brand version disagrees with the UA-string Chrome version
net.ch_ua_vs_ua_browser convictsHTTP Sec-CH-UA brand contradicts the JS UA browser
net.datacenter_origin_proxied convictsWebRTC reveals a datacenter machine hidden behind a non-datacenter connection
net.h2_vs_ua_browser convictsHTTP/2 (Akamai) fingerprint contradicts User-Agent browser
net.no_js_execution convictsPage request with a TLS fingerprint but no browser layer (no JS ran — scripted client)
net.sec_fetch_vs_ua convictsUA claims a modern browser but the request omits Sec-Fetch metadata headers
net.tls_grease_vs_ua convictsUA claims a GREASEing-engine browser (Chromium/Safari) but the TLS ClientHello has no GREASE
net.tls_pq_keyshare_vs_ua convictsUA claims current Chrome but the TLS handshake offers no post-quantum key share
net.tls_vs_ua_browser convictsJA4 browser family contradicts User-Agent browser
net.webrtc_ip_vs_observed corrob.WebRTC-revealed public IP contradicts the observed connection IP (proxied bot)

network 25

net.ch_ua_mobile_no_model convictsMobile Sec-CH-UA-Mobile ?1 but Sec-CH-UA-Model is empty — desktop faking Android
net.ch_ua_no_grease_brand convictsChromium Sec-CH-UA brand list omits the GREASE brand (hardcoded header)
net.ch_ua_on_non_chromium_ua convictsNon-Chromium UA (Safari/Firefox) sends Sec-CH-UA (a Blink-only header) — Chromium faking it
net.fake_declared_crawler convictsA UA declaring a known crawler (Googlebot/Bingbot/…) whose IP fails forward-confirmed reverse DNS
net.h2_continuation_flood convictsHTTP/2 CONTINUATION flood (CVE-2024-27316) on this connection
net.h2_control_flood convictsHTTP/2 control-frame flood (SETTINGS/PING — CVE-2019-9515/9512)
net.h2_header_order_vs_ua convictsChromium UA but the HTTP/2 regular-header order is not chromium-shaped (JA4H)
net.h2_madeyoureset convictsHTTP/2 MadeYouReset stream-reset coercion (CVE-2025-8671) on this connection
net.h2_rapid_reset convictsHTTP/2 rapid-reset flood (CVE-2023-44487) on this connection
net.h2_settings_vs_order convictsHTTP/2 SETTINGS-profile engine contradicts the pseudo-header-order engine
net.h2_unknown_vs_ua convictsModern-browser UA but the HTTP/2 stack matches no known browser engine
net.h2_unstable_within_session convictsHTTP/2 fingerprint (SETTINGS preface) changed within one session
net.h2_vs_tls_browser convictsHTTP/2 fingerprint browser contradicts the TLS (JA4) browser
net.ip_rotation_within_session convictsOne session egressed from many distinct IPs (rotating proxy pool)
net.ja4_tool_vs_ua convictsJA4 is a non-browser HTTP client but the User-Agent claims a browser
net.ja4_unstable_within_session convictsTLS engine (JA4_b cipher identity) changed within one session
net.slow_http_attack convictsHTTP/1.1 slow-header (slowloris) connection hold
net.tcp_os_vs_ua convictsTCP/IP-stack OS kernel contradicts the OS the User-Agent claims
net.tcp_static_window convictsTCP receive window never auto-tuned across the flow (non-kernel stack)
net.tcp_syn_anomaly convictsTCP SYN option VALUES contradict the OS its option order classifies as
net.tls_ext_order_static_within_session convictsChromium-JA4 session repeated one TLS extension order (no per-connection permutation)
net.tls_os_vs_tcp_os convictsJA4-implied OS contradicts TCP/IP-implied OS
net.ua_rotation_within_session convictsOne session sent multiple distinct User-Agent strings (mid-session UA rotation)
net.web_bot_auth_invalid convictsA request presents a Web Bot Auth (RFC 9421) signature that fails Ed25519 verification
net.web_bot_auth_nonce_replay convictsA valid Web Bot Auth (RFC 9421) signature reuses an in-window nonce — a captured-credential replay

browser 119

br.android_mobile_dpr1 convictsAndroid Mobile UA with devicePixelRatio 1 — a phone is never 1x
br.android_model_dpr_incoherent convictsAndroid Pixel model paired with the wrong device-fixed devicePixelRatio
br.android_model_gpu_incoherent convictsAndroid model paired with a mobile GPU family that model never shipped
br.android_model_os_exceeds convictsEOL Android Pixel model paired with an OS version newer than its last update
br.android_model_os_predates convictsAndroid Pixel model paired with an OS version older than the device shipped with
br.android_model_screen_incoherent convictsAndroid device model does not match its device-fixed screen geometry
br.android_no_contacts_api convictsAndroid Chrome Mobile UA lacking navigator.contacts (a mobile-only web API)
br.android_phone_screen_oversized convictsAndroid phone UA reports a screen larger than any real Android phone
br.apple_ua_nonwebkit convictsA UA claiming Apple WebKit (Safari / any iOS browser) exposes a Blink-only structural API
br.audio_noise convictsAudioContext fingerprint differs across identical renders (per-render perturbation)
br.automation_globals convictsAutomation-framework global or webdriver DOM attribute present
br.battery_api_vs_gecko_ua convictsFirefox/Gecko UA that still exposes the Chromium-only Battery API (navigator.getBattery)
br.brave_spoofed convictsnavigator.brave is present but faked (isBrave is not a native function)
br.canvas_geometry_noise convictsCanvas path geometry is non-deterministic (isPointInPath farbling, e.g. JShelter)
br.canvas_lie convictsCanvas/WebGL API tampering detected (getter override)
br.canvas_noise convictsSolid-fill canvas reads back perturbed (engine-level farbling, e.g. Brave)
br.canvas_worker_vs_main convictsCanvas pixel hash differs between the main thread and a Worker OffscreenCanvas
br.cdc_artifacts convictsSelenium / chromedriver automation artifacts in window/document
br.cdp_runtime_enabled convictsCDP Runtime.enable detected (prototype-chain Proxy ownKeys trap)
br.ch_he_headless convictsUA-Client-Hints high-entropy brand list reveals HeadlessChrome
br.ch_he_version_vs_ua convictsUA-CH high-entropy Chrome version contradicts the UA-string version
br.coalesced_untrusted convictsA coalesced pointer batch contains an untrusted (constructor-built) event — fabricated coalescing
br.csp_bypassed convictsPage CSP not enforced (setBypassCSP — an automation context, never a real browser)
br.devicememory_vs_engine convictsFirefox/Safari UA but navigator.deviceMemory (a Blink-only API) is present
br.devicememory_worker_divergence convictsnavigator.deviceMemory differs between the main thread and a Worker
br.domrect_invariant convictsgetBoundingClientRect is non-deterministic or disagrees with getClientRects — geometry shim
br.electron_process convictsRenderer exposes a Node process object — Electron / automation runtime
br.engine_feature_vs_ua convictsJS engine lacks an API its claimed Chrome version shipped — stale template
br.engine_stack_vs_ua convictsJS-engine stack API (Error.stackTraceLimit = V8) contradicts the UA engine
br.error_engine_vs_ua convictsJS-engine error-message format contradicts the UA engine
br.fingerprint_unstable_within_session convictsOne session presented divergent hardware-invariant browser fingerprints (re-randomising anti-detect browser)
br.firefox_ua_nongecko convictsA UA claiming Firefox lacks navigator.buildID — a Gecko-only surface, so it is not Gecko
br.font_mac_internal convictsmacOS internal dot-prefixed system fonts are web-measurable (never on a real Mac)
br.font_os_vs_ua convictsInstalled fonts imply an OS that contradicts the UA platform
br.gpu_uniform_underreport convictsWebGL vertex-uniform limit reported below what the GL compiler actually enforces
br.headless_ua convictsUser-Agent advertises a headless browser
br.honeypot_interaction convictsInteracted with a honeypot bait element a human cannot reach (off-screen + aria-hidden + tabindex -1)
br.iframe_divergence convictsnavigator differs between the main document and an iframe
br.ios_dpr_incoherent convictsiOS UA reports a devicePixelRatio no real iPhone/iPad uses (not 2 or 3)
br.ios_no_standalone convictsiOS UA lacks navigator.standalone (iOS-Safari-only surface) — not genuine iOS Safari
br.ios_screen_desktop_res convictsiOS UA reports a common desktop screen resolution (no iPhone/iPad ships one)
br.ios_screen_offmanifold convictsiOS UA reports a screen matching no real iPhone/iPad geometry (full device set)
br.ios_screen_oversized convictsiOS UA reports a screen larger than any real iPhone/iPad
br.language_vs_languages convictsnavigator.language disagrees with navigator.languages[0] (spec-invariant violation)
br.languages_worker_vs_main convictsnavigator.languages differs between the main thread and a Web Worker
br.measuretext_offscreen_vs convictsCanvas measureText differs between main and OffscreenCanvas — main-thread font hook
br.mobile_cores_high convictsMobile UA exposes more than 8 CPU cores — a phone SoC does not
br.mobile_gpu_caps_mismatch convictsHigh-end mobile GPU string but MAX_TEXTURE_SIZE below the 16384 mobile floor
br.mobile_gpu_not_mobile convictsMobile UA renders WebGL on a non-mobile GPU (desktop/software renderer)
br.mobile_gpu_uniforms_software convictsMobile GPU string with software/desktop-level vertex or fragment uniform capacity
br.mobile_no_js_model convictsMobile userAgentData but getHighEntropyValues().model is empty — desktop faking Android (JS surface)
br.mobile_no_touch convictsPhone/tablet UA reports no touch capability (maxTouchPoints 0)
br.mobile_pointer_hover_desktop convictsMobile UA but the primary pointer is fine / a hovering pointer exists (desktop pointer surface)
br.native_invariant_violated convictsA built-in method claims native but violates native-function invariants
br.nav_property_spoofed convictsA prototype-inherited navigator property (pdfViewerEnabled/mimeTypes) is an own property
br.navplatform_vs_ua convictsnavigator.platform implies an OS that contradicts the UA platform
br.no_chrome_object convictsChrome User-Agent but window.chrome is absent
br.notification_getter_tampered convictsNotification.permission getter is non-native (faked to beat the notification floor)
br.oscpu_vs_ua convictsnavigator.oscpu implies an OS that contradicts the UA platform
br.permissions_anomaly convictsNotification.permission contradicts the Permissions API state
br.plugins_spoofed convictsnavigator.plugins is an own property (patched via defineProperty)
br.pointer_touch_incoherent convictsCSS coarse-pointer and navigator.maxTouchPoints disagree on touch
br.productsub_vs_ua convictsnavigator.productSub render engine contradicts the UA
br.readback_noise convictsAudioBuffer getChannelData and copyFromChannel disagree — readback noise shim
br.safari_ua_no_webkit_api convictsA UA claiming Safari lacks window.GestureEvent — a WebKit-only surface, so it is not WebKit
br.screen_depth_incoherent convictsscreen.pixelDepth and screen.colorDepth disagree (aliases that are always equal on a real display)
br.screen_impossible convictsAvailable screen area exceeds the physical screen (impossible geometry)
br.timezone_inconsistent convictsIANA timeZone and getTimezoneOffset() disagree (a real browser derives both from the OS)
br.timezone_offset_vs_intl convictsgetTimezoneOffset disagrees with the Intl IANA timezone's actual offset
br.timezone_worker_vs_main convictsTimezone differs between the main thread and a Web Worker
br.tostring_tampered convictsA native function's toString no longer reads [native code]
br.ua_platform_vs_ch_platform convictsnavigator/UA platform contradicts Sec-CH-UA-Platform
br.uadata_model_worker_divergence convictsgetHighEntropyValues().model differs between the main thread and a Worker
br.uadata_worker_divergence convictsnavigator.userAgentData differs between the main thread and a Worker
br.vendor_vs_ua convictsnavigator.vendor implies an engine that contradicts the UA
br.voice_os_vs_ua convictsInstalled TTS voices imply an OS that contradicts the UA platform
br.webdriver_getter_tampered convictsNavigator.prototype.webdriver getter is non-native (prototype patch)
br.webdriver_present convictsnavigator.webdriver is true
br.webdriver_spoofed convictsnavigator.webdriver is an own property (patched via defineProperty)
br.webgl_caps_worker_vs_main convictsWebGL capability vector (limits + extensions) differs between the main thread and a Worker OffscreenCanvas
br.webgl_getparameter_tampered convictsWebGLRenderingContext.getParameter is overridden (non-native toString)
br.webgl_maxtexture_unallocatable convictsWebGL MAX_TEXTURE_SIZE claimed but a texture of that dimension cannot be allocated
br.webgl_os_vs_ua convictsWebGL renderer implies an OS that contradicts the UA platform
br.webgl_renderer_artifact convictsWebGL renderer string is an anti-detect spoofing placeholder, not a real driver
br.webgl_renderer_caps_mismatch convictsWebGL renderer names a high-end GPU but the hardware capabilities are below its floor (spoofed string)
br.webgl_worker_vs_main convictsWebGL GPU renderer differs between the main thread and a Worker OffscreenCanvas
br.webgpu_vendor_vs_webgl convictsWebGPU adapter GPU family contradicts the WebGL renderer (spoofed renderer on real hardware)
br.worker_constructor_tampered convictsWeb Worker / OffscreenCanvas constructor is not native (worker-realm spoof injection)
br.worker_divergence convictsnavigator differs between the main thread and a Web Worker
br.worker_removed convictswindow.Worker absent while the collector runs — the Worker realm was stripped to hide from divergence guards
br.worker_source_rewritten convictsWeb Worker ran from a different source than the page passed (worker-scope spoof injection)
br.adblock_present corrob.A content blocker hides the ad bait element (Camoufox default-bundles uBlock Origin)
br.audio_missing corrob.No OfflineAudioContext (audio stack absent)
br.codec_os_incoherent corrob.Non-Linux UA cannot play H.264/AAC (proprietary codecs a real Windows/macOS has via OS)
br.color_depth_anomaly corrob.screen.colorDepth is not a real-display value (24/30/32)
br.devicepixelratio_anomaly corrob.window.devicePixelRatio is not a positive finite number
br.fingerprint_improbable corrob.Fingerprint joint (platform/gpu/screen/colour/cores) is improbable under the real-traffic prior
br.font_linux_leak corrob.Linux metric-compatible fonts (Arimo/Cousine/Tinos) present under a non-Linux UA
br.hover_none_desktop corrob.Non-mobile UA reports no hover capability (CSS hover:none)
br.languages_empty corrob.navigator.languages is empty
br.low_hardware_concurrency corrob.navigator.hardwareConcurrency implausibly low
br.macos_dpr1 corrob.macOS UA but devicePixelRatio is 1.0 (a modern Mac is Retina, dPR 2)
br.media_devices_empty corrob.navigator.mediaDevices.enumerateDevices() is empty (no audio/video hardware)
br.mimetypes_empty corrob.navigator.mimeTypes is empty
br.no_connection corrob.Chromium UA but navigator.connection is absent
br.no_devicememory corrob.Chromium UA but navigator.deviceMemory is undefined
br.no_pdfviewer corrob.Chromium UA but pdfViewerEnabled is false
br.no_plugins corrob.Desktop browser reports zero plugins
br.platform_empty corrob.navigator.platform is empty
br.rfp_browser corrob.resistFingerprinting signature (UTC + letterboxed window + 2-core, all at once)
br.screen_avail_invalid corrob.screen.availWidth/Height exceeds the physical screen size
br.screen_zero corrob.Screen or outer window dimensions are zero
br.voices_empty corrob.No speech-synthesis voices (headless/container — a real desktop has OS TTS)
br.webgl2_missing corrob.WebGL2 unsupported (headless software rendering)
br.webgl_not_angle corrob.Chromium UA but the WebGL renderer is not ANGLE-wrapped — renderer spoof
br.webgl_perf_vs_renderer corrob.WebGL renderer names hardware but renders at software speed — the substance behind the string
br.webgl_software corrob.WebGL renderer is software (SwiftShader / llvmpipe / Mesa) — headless tell
br.webgpu_webgl_vs corrob.WebGL renderer claims a hardware GPU but WebGPU exposes no real adapter (spoofed renderer)
br.webrtc_unavailable corrob.WebRTC gathers no ICE candidates (disabled/blocked)

behavioral 38

bh.arena_audio_superhuman convictsAudio CAPTCHA answered correctly faster than its clip plays — ASR automation, not a listener
bh.arena_captcha_superhuman convictsCAPTCHA answered correctly faster than a human can read and respond — automated solver
bh.arena_count_superhuman convictsCounting gate answered correctly faster than a human can scan the scene
bh.arena_hold_robotic convictsPress-and-hold gate solved with a static (no-tremor) hold or an impossible claimed hold time
bh.arena_honeypot_filled convictsFilled a hidden honeypot form field — a naive form-filling bot, never a human
bh.arena_keymap_no_exploration convictsRemapped-keyboard target typed correctly with zero exploration — the mapping was decoded, not discovered
bh.arena_localize_superhuman convictsPoint-localization gate solved with a pixel-perfect centroid click or impossibly fast
bh.arena_match_superhuman convictsOrientation-match gate solved faster than a human can scan a reference plus N candidates
bh.arena_pattern_superhuman convictsTrace-the-pattern stroke hugs the ideal path too closely (no hand tremor) or is impossibly fast
bh.arena_pursuit_superhuman convictsSmooth-pursuit tracking held within a few px of the moving target (superhuman tracking accuracy)
bh.arena_queue_bypass convictsTook a virtual-queue protected action BEFORE admission — skipped the wait, a queue-bypass bot
bh.arena_queue_hoarding convictsOne session holding many concurrent virtual-queue positions — a position-hoarding scalper
bh.arena_queue_superhuman convictsActed on a virtual-queue admission faster than a human can perceive it — position-holding bot
bh.arena_reaction_superhuman convictsReaction-time gate answered faster than the human physiological floor (or before the go cue)
bh.arena_seqclick_superhuman convictsOrdered click-in-sequence gate solved faster than human target-location or at a metronomic cadence
bh.arena_shell_precomputed convictsShell-game answered correctly before the shuffle could finish — swap payload precomputed, not watched
bh.arena_slide_superhuman convictsSliding-tile puzzle solved with an optimal (minimum-move) plan or impossibly fast
bh.arena_spotdiff_superhuman convictsSpot-the-difference solved with pixel-perfect diff clicks (image-diff) or superhuman scan speed
bh.arena_stale_snapshot convictsActed on a moving target's SECONDS-OLD position — a snapshot-then-slowly-reason LLM agent
bh.arena_timing_superhuman convictsMotor-timing gate solved with superhuman release precision or impossible claimed hold time
bh.arena_trajectory_forged convictsSlider/rotate drag trajectory claims more drag-time than the whole server-observed solve — synthesized
bh.session_flow_robotic convictsMulti-step gate flow paced at machine-regular intervals (near-zero inter-step CV — a timer, not a human)
bh.session_flow_superhuman convictsMulti-step gate flow completed at superhuman inter-step cadence (session-intent, not a single step)
bh.trace_replay_within_session convictsOne session replayed an identical pointer trajectory across page loads (record-and-replay bot)
bh.action_cadence_deliberative corrob.Metronomic inter-action cadence at LLM-inference latency
bh.click_without_trajectory corrob.Trusted mouse click with no pointer trajectory in the session (teleport-click)
bh.input_entropy_floor corrob.Mouse-movement entropy below the human floor
bh.input_via_paste corrob.Form value injected with no keystroke or trusted paste (programmatic input)
bh.keystroke_entropy_floor corrob.Keystroke timing entropy below the human floor
bh.keystroke_interval_floor corrob.Median inter-keystroke interval below the human cadence floor
bh.mobile_keystroke_interval_floor corrob.Mobile session typing faster than any human thumb cadence
bh.no_input_before_action corrob.Action (submit/nav) with zero pointer events
bh.path_too_straight corrob.Mouse path is unnaturally straight (start->end ~= total length)
bh.power_law_violation corrob.Mouse motion violates the 2/3 power law (speed independent of curvature — synthetic path)
bh.scroll_teleport corrob.Programmatic scroll jump with no wheel/scroll-key input
bh.synthetic_no_coalesced corrob.Long pointer stream never coalesces (CDP-injected input, not hardware)
bh.touch_uniform_velocity corrob.Touch-swipe velocity too uniform for a human finger (mobile)
bh.uniform_velocity corrob.Mouse moves at near-constant speed (low velocity CV)

reputation 4

rep.abuse_listed corrob.Source IP is on an abuse/threat reputation list
rep.datacenter_asn corrob.Source IP belongs to a datacenter / hosting ASN
rep.known_proxy_exit corrob.Source IP is a known VPN / proxy / Tor exit
rep.webrtc_origin_datacenter corrob.WebRTC-leaked real origin IP belongs to a datacenter / hosting ASN