[{"data":1,"prerenderedAt":2436},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fbot-detection":64,"navigation":217,"navLinks_footer":842,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate_page":855,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate_surround":1783,"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate":1786},{"id":4,"extension":5,"links":6,"meta":61,"stem":62,"__hash__":63},"navigationMenu\u002Fnavigation.json","json",[7,52,57],{"nested":8,"label":9,"icon":10,"to":11,"children":12},true,"Docs","i-lucide-book-open","\u002Fdocs\u002Fgetting-started",[13,19,26,32,39,45],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},"Getting Started","i-lucide-rocket","An introduction to help you understand the core components.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fdocshub","Start Here",{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},"Auth H3 Client","i-lucide-key-round","\u002Fdocs\u002Fauth-h3client","Seamlessly enforce OAuth 2.0 authentication and session management integrated directly as the client of the IAM module.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth-h3client","Core",{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},"IAM","i-lucide-shield-check","\u002Fdocs\u002Fiam","Identity and Access Management featuring granular roles, permissions, and security policies.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fauth",{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},"Bot Detection","i-lucide-cpu","\u002Fdocs\u002Fbot-detection","Advanced behavioral analysis and request fingerprinting to stop malicious automated traffic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fbot-detector","Security",{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},"Shield Base","i-lucide-database-zap","\u002Fdocs\u002Fshield-base","CLI and programmatic toolkit for compiling offline-ready IP intelligence databases from BGP, GeoIP, Tor, FireHOL, and other public threat feeds.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Fshield-base-cli",{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},"Utils","i-lucide-wrench","\u002Fdocs\u002Futils","A standard library of highly optimized helpers for formatting, validation, and core logic.","https:\u002F\u002Fgithub.com\u002FSergo706\u002Futils","Library",{"nested":53,"label":54,"icon":55,"to":56},false,"Blog","i-lucide-pen-line","\u002Fblog",{"nested":53,"label":58,"icon":59,"to":60},"Website","lucide:app-window-mac","https:\u002F\u002Friavzon.com",{},"navigation","gkaQ0xRGxSLrLyM3kttLe0oBwkrR1EBjlepF8LSbwF8",[65],{"title":9,"path":66,"stem":67,"children":68,"page":53},"\u002Fdocs","docs",[69],{"title":70,"path":35,"stem":71,"children":72,"icon":74},"Bot Detector","docs\u002Fbot-detection\u002Findex",[73,75,78,83,88,112,203,207,212],{"title":70,"path":35,"stem":71,"icon":74},"i-lucide-shield-half",{"title":14,"path":76,"stem":77,"icon":15},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":79,"path":80,"stem":81,"icon":82},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli","i-lucide-terminal",{"title":84,"path":85,"stem":86,"icon":87},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources","i-lucide-database",{"title":89,"icon":10,"path":90,"stem":91,"children":92,"page":53},"Guides","\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[93,98,102,107],{"title":94,"path":95,"stem":96,"icon":97},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM","i-lucide-puzzle",{"title":99,"path":100,"stem":101,"icon":41},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":103,"path":104,"stem":105,"icon":106},"Logging","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING","i-lucide-scroll-text",{"title":108,"path":109,"stem":110,"icon":111},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE","i-lucide-chart-line",{"title":113,"path":114,"stem":115,"children":116,"icon":28},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[117,118,123,128,133,138,143,148,153,158,163,168,173,178,183,188,193,198],{"title":113,"path":114,"stem":115,"icon":28},{"title":119,"path":120,"stem":121,"icon":122},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation","i-lucide-network",{"title":124,"path":125,"stem":126,"icon":127},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots","i-lucide-bot",{"title":129,"path":130,"stem":131,"icon":132},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device","i-lucide-monitor-smartphone",{"title":134,"path":135,"stem":136,"icon":137},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map","i-lucide-languages",{"title":139,"path":140,"stem":141,"icon":142},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats","i-lucide-shield-alert",{"title":144,"path":145,"stem":146,"icon":147},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification","i-lucide-server",{"title":149,"path":150,"stem":151,"icon":152},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis","i-lucide-globe-lock",{"title":154,"path":155,"stem":156,"icon":157},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency","i-lucide-clock",{"title":159,"path":160,"stem":161,"icon":162},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot","i-lucide-bug",{"title":164,"path":165,"stem":166,"icon":167},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips","i-lucide-ban",{"title":169,"path":170,"stem":171,"icon":172},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate","i-lucide-activity",{"title":174,"path":175,"stem":176,"icon":177},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies","i-lucide-waypoints",{"title":179,"path":180,"stem":181,"icon":182},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence","i-lucide-navigation",{"title":184,"path":185,"stem":186,"icon":187},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint","i-lucide-timer",{"title":189,"path":190,"stem":191,"icon":192},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header","i-lucide-scan-search",{"title":194,"path":195,"stem":196,"icon":197},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation","i-lucide-map-pin",{"title":199,"path":200,"stem":201,"icon":202},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua","i-lucide-search-x",{"title":38,"path":204,"stem":205,"icon":206},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security","i-lucide-lock",{"title":208,"path":209,"stem":210,"icon":211},"API Reference","\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api","i-lucide-code",{"title":213,"path":214,"stem":215,"icon":216},"Configuration","\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration","i-lucide-settings",[218],{"title":9,"path":66,"stem":67,"children":219,"page":53},[220,364,399,404,582,649],{"title":20,"path":22,"stem":221,"children":222},"docs\u002Fauth-h3client\u002Findex",[223,224,233,269,295,317,320,340,343],{"title":20,"path":22,"stem":221},{"title":14,"path":225,"stem":226,"children":227},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[228,229],{"title":14,"path":225,"stem":226},{"title":230,"path":231,"stem":232},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":234,"path":235,"stem":236,"children":237},"Essentials","\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[238,239,243,247,251,255,259,262,266],{"title":234,"path":235,"stem":236},{"title":240,"path":241,"stem":242},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":244,"path":245,"stem":246},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":248,"path":249,"stem":250},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":252,"path":253,"stem":254},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":256,"path":257,"stem":258},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":260,"stem":261},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":263,"path":264,"stem":265},"Cookies","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":103,"path":267,"stem":268},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":270,"path":271,"stem":272,"children":273},"MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[274,275,279,283,287,291],{"title":270,"path":271,"stem":272},{"title":276,"path":277,"stem":278},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":280,"path":281,"stem":282},"Password Reset","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":284,"path":285,"stem":286},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":288,"path":289,"stem":290},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":292,"path":293,"stem":294},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":296,"path":297,"stem":298,"children":299},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[300,301,305,309,313],{"title":296,"path":297,"stem":298},{"title":302,"path":303,"stem":304},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":306,"path":307,"stem":308},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":310,"path":311,"stem":312},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":314,"path":315,"stem":316},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":318,"stem":319},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":89,"path":321,"stem":322,"children":323,"page":53},"\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[324,328,332,336],{"title":325,"path":326,"stem":327},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":329,"path":330,"stem":331},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":333,"path":334,"stem":335},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":337,"path":338,"stem":339},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":213,"path":341,"stem":342},"\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":208,"path":344,"stem":345,"children":346},"\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[347,348,352,356,360],{"title":208,"path":344,"stem":345},{"title":349,"path":350,"stem":351},"Routes Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":353,"path":354,"stem":355},"Middleware Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":357,"path":358,"stem":359},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":361,"path":362,"stem":363},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",{"title":70,"path":35,"stem":71,"children":365},[366,367,368,369,370,376,396,397,398],{"title":70,"path":35,"stem":71},{"title":14,"path":76,"stem":77},{"title":79,"path":80,"stem":81},{"title":84,"path":85,"stem":86},{"title":89,"path":90,"stem":91,"children":371,"page":53},[372,373,374,375],{"title":94,"path":95,"stem":96},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":108,"path":109,"stem":110},{"title":113,"path":114,"stem":115,"children":377},[378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395],{"title":113,"path":114,"stem":115},{"title":119,"path":120,"stem":121},{"title":124,"path":125,"stem":126},{"title":129,"path":130,"stem":131},{"title":134,"path":135,"stem":136},{"title":139,"path":140,"stem":141},{"title":144,"path":145,"stem":146},{"title":149,"path":150,"stem":151},{"title":154,"path":155,"stem":156},{"title":159,"path":160,"stem":161},{"title":164,"path":165,"stem":166},{"title":169,"path":170,"stem":171},{"title":174,"path":175,"stem":176},{"title":179,"path":180,"stem":181},{"title":184,"path":185,"stem":186},{"title":189,"path":190,"stem":191},{"title":194,"path":195,"stem":196},{"title":199,"path":200,"stem":201},{"title":38,"path":204,"stem":205},{"title":208,"path":209,"stem":210},{"title":213,"path":214,"stem":215},{"title":400,"path":11,"stem":401,"children":402},"Introduction","docs\u002Fgetting-started\u002Findex",[403],{"title":400,"path":11,"stem":401},{"title":27,"path":29,"stem":405,"children":406},"docs\u002Fiam\u002Findex",[407,408,411,546,549,565,568],{"title":27,"path":29,"stem":405},{"title":14,"path":409,"stem":410},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":234,"path":412,"stem":413,"children":414},"\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[415,416,420,424,428,432,436,440,444,448,452,456,459,463,467,471,475,478,482,486,489,493,496],{"title":234,"path":412,"stem":413},{"title":417,"path":418,"stem":419},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":421,"path":422,"stem":423},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":425,"path":426,"stem":427},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":429,"path":430,"stem":431},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":433,"path":434,"stem":435},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":437,"path":438,"stem":439},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":441,"path":442,"stem":443},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":445,"path":446,"stem":447},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":449,"path":450,"stem":451},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":453,"path":454,"stem":455},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":270,"path":457,"stem":458},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":460,"path":461,"stem":462},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":464,"path":465,"stem":466},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":468,"path":469,"stem":470},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":472,"path":473,"stem":474},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":103,"path":476,"stem":477},"\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":479,"path":480,"stem":481},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":483,"path":484,"stem":485},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":263,"path":487,"stem":488},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":490,"path":491,"stem":492},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":280,"path":494,"stem":495},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":497,"path":498,"stem":499,"children":500},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[501,502,506,510,540,543],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":507,"path":508,"stem":509},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":511,"path":512,"stem":513,"children":514},"Management","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[515,516,520,524,528,532,536],{"title":511,"path":512,"stem":513},{"title":517,"path":518,"stem":519},"Privilege and Scopes","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":521,"path":522,"stem":523},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":525,"path":526,"stem":527},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":529,"path":530,"stem":531},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":533,"path":534,"stem":535},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":537,"path":538,"stem":539},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":479,"path":541,"stem":542},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":544,"stem":545},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":547,"stem":548},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":89,"path":550,"stem":551,"children":552,"page":53},"\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[553,557,561],{"title":554,"path":555,"stem":556},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":558,"path":559,"stem":560},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":562,"path":563,"stem":564},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":213,"path":566,"stem":567},"\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":569,"path":570,"stem":571,"children":572,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[573,576,579],{"title":208,"path":574,"stem":575},"\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":353,"path":577,"stem":578},"\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":349,"path":580,"stem":581},"\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",{"title":40,"path":42,"stem":583,"children":584},"docs\u002Fshield-base\u002Findex",[585,586,589,593,634,638,642,646],{"title":40,"path":42,"stem":583},{"title":14,"path":587,"stem":588},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":590,"path":591,"stem":592},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":84,"path":594,"stem":595,"children":596},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[597,598,602,606,610,614,618,622,626,630],{"title":84,"path":594,"stem":595},{"title":599,"path":600,"stem":601},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":603,"path":604,"stem":605},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":607,"path":608,"stem":609},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":611,"path":612,"stem":613},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":615,"path":616,"stem":617},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":619,"path":620,"stem":621},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":623,"path":624,"stem":625},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":627,"path":628,"stem":629},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":631,"path":632,"stem":633},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":635,"path":636,"stem":637},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":639,"path":640,"stem":641},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":643,"path":644,"stem":645},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":208,"path":647,"stem":648},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":361,"path":48,"stem":650,"children":651},"docs\u002Futils\u002Findex",[652,653,670,703,800],{"title":361,"path":48,"stem":650},{"title":654,"path":655,"stem":656,"children":657,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[658,662,666],{"title":659,"path":660,"stem":661},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":663,"path":664,"stem":665},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":667,"path":668,"stem":669},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":671,"path":672,"stem":673,"children":674,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[675,679,683,687,691,695,699],{"title":676,"path":677,"stem":678},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":680,"path":681,"stem":682},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":684,"path":685,"stem":686},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":688,"path":689,"stem":690},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":692,"path":693,"stem":694},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":696,"path":697,"stem":698},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":700,"path":701,"stem":702},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":704,"path":705,"stem":706,"children":707,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[708,712,716,720,724,728,732,736,740,744,748,752,756,760,764,768,772,776,780,784,788,792,796],{"title":709,"path":710,"stem":711},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":713,"path":714,"stem":715},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":717,"path":718,"stem":719},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":721,"path":722,"stem":723},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":725,"path":726,"stem":727},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":729,"path":730,"stem":731},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":733,"path":734,"stem":735},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":737,"path":738,"stem":739},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":741,"path":742,"stem":743},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":745,"path":746,"stem":747},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":749,"path":750,"stem":751},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":753,"path":754,"stem":755},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":757,"path":758,"stem":759},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":761,"path":762,"stem":763},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":765,"path":766,"stem":767},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":769,"path":770,"stem":771},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":773,"path":774,"stem":775},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":777,"path":778,"stem":779},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":781,"path":782,"stem":783},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":785,"path":786,"stem":787},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":789,"path":790,"stem":791},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":793,"path":794,"stem":795},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":797,"path":798,"stem":799},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":801,"path":802,"stem":803,"children":804},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[805,806,810,814,818,822,826,830,834,838],{"title":801,"path":802,"stem":803},{"title":807,"path":808,"stem":809},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":811,"path":812,"stem":813},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":815,"path":816,"stem":817},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":819,"path":820,"stem":821},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":823,"path":824,"stem":825},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":827,"path":828,"stem":829},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":831,"path":832,"stem":833},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":835,"path":836,"stem":837},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":839,"path":840,"stem":841},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":843,"meta":854,"stem":62,"__hash__":63},[844,852,853],{"nested":8,"label":9,"icon":10,"to":11,"children":845},[846,847,848,849,850,851],{"label":14,"icon":15,"to":11,"description":16,"github":17,"badge":18},{"label":20,"icon":21,"to":22,"description":23,"github":24,"badge":25},{"label":27,"icon":28,"to":29,"description":30,"github":31,"badge":25},{"label":33,"icon":34,"to":35,"description":36,"github":37,"badge":38},{"label":40,"icon":41,"to":42,"description":43,"github":44,"badge":38},{"label":46,"icon":47,"to":48,"description":49,"github":50,"badge":51},{"nested":53,"label":54,"icon":55,"to":56},{"nested":53,"label":58,"icon":59,"to":60},{},{"id":856,"title":99,"body":857,"description":1776,"extension":1777,"icon":41,"meta":1778,"module":1779,"navigation":8,"path":100,"rawbody":1780,"seo":1781,"stem":101,"__hash__":1782},"docs\u002Fdocs\u002Fbot-detection\u002F03.guides\u002FGENERATE.md",{"type":858,"value":859,"toc":1762},"minimark",[860,880,883,886,891,901,924,955,958,960,964,971,984,986,989,1000,1163,1235,1244,1246,1250,1254,1257,1327,1333,1337,1344,1390,1399,1401,1405,1408,1411,1429,1432,1447,1453,1457,1463,1665,1667,1676,1684,1734,1758],[861,862,863,867,868,871,872,875,876,879],"p",{},[864,865,866],"code",{},"bot-detector generate"," reads your accumulated visitor and ban history and compiles it into two binary MMDB databases: ",[864,869,870],{},"banned.mmdb"," and ",[864,873,874],{},"highRisk.mmdb",". Once compiled, the ",[877,878,164],"a",{"href":165}," checker reads these files in the cheap phase, rejecting repeat offenders in microseconds without running the full 17-checker pipeline.",[861,881,882],{},"The databases do not exist on a fresh installation. They grow as your application accumulates traffic and ban records, and they only become useful once you run generation for the first time.",[884,885],"hr",{},[887,888,890],"h2",{"id":889},"what-generation-produces","What Generation Produces",[861,892,893,894,896,897,900],{},"Running ",[864,895,866],{}," compiles two files into ",[864,898,899],{},"_data-sources\u002F"," inside the package directory. Both run in parallel.",[861,902,903,908,909,912,913,916,917,919,920,923],{},[904,905,906],"strong",{},[864,907,870],{},": built from every row in the ",[864,910,911],{},"banned"," table that has a non-null ",[864,914,915],{},"ip_address",". Each entry stores the IP, score, country, user agent, and reason codes from the ban event. The ",[877,918,164],{"href":165}," checker reads this file and triggers an immediate ",[864,921,922],{},"BAD_BOT_DETECTED"," ban for any matching IP, skipping all other checkers.",[861,925,926,930,931,934,935,938,939,942,943,946,947,950,951,954],{},[904,927,928],{},[864,929,874],{},": built from rows in the ",[864,932,933],{},"visitors"," table where ",[864,936,937],{},"suspicious_activity_score"," is at or above ",[864,940,941],{},"generator.scoreThreshold"," (default ",[864,944,945],{},"70","). These are visitors who accumulated a suspicious score but were never banned. On their next visit, they receive ",[864,948,949],{},"highRiskPenalty"," points in the cheap phase, bringing them closer to ",[864,952,953],{},"banScore"," with less effort from other checkers.",[861,956,957],{},"If either table has no qualifying rows, the corresponding file is skipped silently. The checker handles missing files by skipping that lookup.",[884,959],{},[887,961,963],{"id":962},"hot-reload","Hot Reload",[861,965,966,967,970],{},"The MMDB files are opened with ",[864,968,969],{},"watchForUpdates: true",". When a new file is written to disk, the reader reloads it automatically without restarting your application. You can run generation while traffic is live and the updated databases take effect within seconds.",[972,973,974],"note",{},[861,975,976,979,980,983],{},[864,977,978],{},"watchForUpdates"," is disabled during tests (",[864,981,982],{},"NODE_ENV=test","). In all other environments it is always on.",[884,985],{},[887,987,213],{"id":988},"configuration",[861,990,991,992,995,996,999],{},"The ",[864,993,994],{},"generator"," block in ",[864,997,998],{},"defineConfiguration"," controls all generation behavior.",[1001,1002,1008],"pre",{"className":1003,"code":1004,"filename":1005,"language":1006,"meta":1007,"style":1007},"language-ts shiki shiki-themes light-plus light-plus dracula","await defineConfiguration({\n  store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } },\n  generator: {\n    scoreThreshold: 70,\n    deleteAfterBuild: false,\n    mmdbctlPath: 'mmdbctl',\n    generateTypes: false,\n  },\n})\n","server.ts","ts","",[864,1009,1010,1027,1082,1093,1108,1122,1139,1151,1157],{"__ignoreMap":1007},[1011,1012,1015,1019,1023],"span",{"class":1013,"line":1014},"line",1,[1011,1016,1018],{"class":1017},"sZ328","await",[1011,1020,1022],{"class":1021},"sHOzp"," defineConfiguration",[1011,1024,1026],{"class":1025},"sDd4n","({\n",[1011,1028,1030,1034,1038,1041,1044,1046,1048,1051,1053,1057,1061,1064,1067,1070,1072,1074,1077,1079],{"class":1013,"line":1029},2,[1011,1031,1033],{"class":1032},"sjsA6","  store",[1011,1035,1037],{"class":1036},"s34zl",":",[1011,1039,1040],{"class":1025}," { ",[1011,1042,1043],{"class":1032},"main",[1011,1045,1037],{"class":1036},[1011,1047,1040],{"class":1025},[1011,1049,1050],{"class":1032},"driver",[1011,1052,1037],{"class":1036},[1011,1054,1056],{"class":1055},"sFkSl"," '",[1011,1058,1060],{"class":1059},"sFB1V","sqlite",[1011,1062,1063],{"class":1055},"'",[1011,1065,1066],{"class":1025},", ",[1011,1068,1069],{"class":1032},"name",[1011,1071,1037],{"class":1036},[1011,1073,1056],{"class":1055},[1011,1075,1076],{"class":1059},".\u002Fbot-detector.db",[1011,1078,1063],{"class":1055},[1011,1080,1081],{"class":1025}," } },\n",[1011,1083,1085,1088,1090],{"class":1013,"line":1084},3,[1011,1086,1087],{"class":1032},"  generator",[1011,1089,1037],{"class":1036},[1011,1091,1092],{"class":1025}," {\n",[1011,1094,1096,1099,1101,1105],{"class":1013,"line":1095},4,[1011,1097,1098],{"class":1032},"    scoreThreshold",[1011,1100,1037],{"class":1036},[1011,1102,1104],{"class":1103},"spgvN"," 70",[1011,1106,1107],{"class":1025},",\n",[1011,1109,1111,1114,1116,1120],{"class":1013,"line":1110},5,[1011,1112,1113],{"class":1032},"    deleteAfterBuild",[1011,1115,1037],{"class":1036},[1011,1117,1119],{"class":1118},"sjR7W"," false",[1011,1121,1107],{"class":1025},[1011,1123,1125,1128,1130,1132,1135,1137],{"class":1013,"line":1124},6,[1011,1126,1127],{"class":1032},"    mmdbctlPath",[1011,1129,1037],{"class":1036},[1011,1131,1056],{"class":1055},[1011,1133,1134],{"class":1059},"mmdbctl",[1011,1136,1063],{"class":1055},[1011,1138,1107],{"class":1025},[1011,1140,1142,1145,1147,1149],{"class":1013,"line":1141},7,[1011,1143,1144],{"class":1032},"    generateTypes",[1011,1146,1037],{"class":1036},[1011,1148,1119],{"class":1118},[1011,1150,1107],{"class":1025},[1011,1152,1154],{"class":1013,"line":1153},8,[1011,1155,1156],{"class":1025},"  },\n",[1011,1158,1160],{"class":1013,"line":1159},9,[1011,1161,1162],{"class":1025},"})\n",[1164,1165,1166,1183,1208,1225],"field-group",{},[1167,1168,1171],"field",{"name":1169,"type":1170},"scoreThreshold","number",[861,1172,1173,1174,1176,1177,1179,1180,1182],{},"Minimum ",[864,1175,937],{}," for a visitor row to be included in ",[864,1178,874],{},". Lower values include more visitors; higher values are more conservative. Default: ",[864,1181,945],{},".",[1167,1184,1187],{"name":1185,"type":1186},"deleteAfterBuild","boolean",[861,1188,1189,1190,1193,1194,1196,1197,1199,1200,1196,1202,1204,1205,1182],{},"When ",[864,1191,1192],{},"true",", rows compiled into ",[864,1195,870],{}," are deleted from the ",[864,1198,911],{}," table, and high-risk rows compiled into ",[864,1201,874],{},[864,1203,933],{}," table. This keeps the database lean but means the raw rows are gone after generation. Default: ",[864,1206,1207],{},"false",[1167,1209,1212],{"name":1210,"type":1211},"mmdbctlPath","string",[861,1213,1214,1215,1217,1218,1221,1222,1182],{},"Path to the ",[864,1216,1134],{}," binary used to compile MMDB files. If the binary is not on ",[864,1219,1220],{},"PATH",", provide the absolute path here. Default: ",[864,1223,1224],{},"'mmdbctl'",[1167,1226,1228],{"name":1227,"type":1186},"generateTypes",[861,1229,1189,1230,1232,1233,1182],{},[864,1231,1192],{},", TypeScript type definitions are generated alongside the MMDB files. Useful during development. Default: ",[864,1234,1207],{},[1236,1237,1238],"warning",{},[861,1239,1240,1243],{},[864,1241,1242],{},"deleteAfterBuild: true"," permanently removes rows from your database after each generation. If generation fails mid-run, some rows may already be deleted. Only enable this if your database size is a concern and you have another record of your ban history.",[884,1245],{},[887,1247,1249],{"id":1248},"running-generation","Running Generation",[1251,1252,79],"h3",{"id":1253},"cli",[861,1255,1256],{},"Run generation from the terminal at any time:",[1258,1259,1260,1279,1296,1312],"code-group",{},[1001,1261,1266],{"className":1262,"code":1263,"filename":1264,"language":1265,"meta":1007,"style":1007},"language-bash shiki shiki-themes light-plus light-plus dracula","npx @riavzon\u002Fbot-detector generate\n","npm","bash",[864,1267,1268],{"__ignoreMap":1007},[1011,1269,1270,1273,1276],{"class":1013,"line":1014},[1011,1271,1272],{"class":1021},"npx",[1011,1274,1275],{"class":1059}," @riavzon\u002Fbot-detector",[1011,1277,1278],{"class":1059}," generate\n",[1001,1280,1283],{"className":1262,"code":1281,"filename":1282,"language":1265,"meta":1007,"style":1007},"pnpm dlx @riavzon\u002Fbot-detector generate\n","pnpm",[864,1284,1285],{"__ignoreMap":1007},[1011,1286,1287,1289,1292,1294],{"class":1013,"line":1014},[1011,1288,1282],{"class":1021},[1011,1290,1291],{"class":1059}," dlx",[1011,1293,1275],{"class":1059},[1011,1295,1278],{"class":1059},[1001,1297,1300],{"className":1262,"code":1298,"filename":1299,"language":1265,"meta":1007,"style":1007},"yarn dlx @riavzon\u002Fbot-detector generate\n","yarn",[864,1301,1302],{"__ignoreMap":1007},[1011,1303,1304,1306,1308,1310],{"class":1013,"line":1014},[1011,1305,1299],{"class":1021},[1011,1307,1291],{"class":1059},[1011,1309,1275],{"class":1059},[1011,1311,1278],{"class":1059},[1001,1313,1316],{"className":1262,"code":1314,"filename":1315,"language":1265,"meta":1007,"style":1007},"bunx @riavzon\u002Fbot-detector generate\n","bun",[864,1317,1318],{"__ignoreMap":1007},[1011,1319,1320,1323,1325],{"class":1013,"line":1014},[1011,1321,1322],{"class":1021},"bunx",[1011,1324,1275],{"class":1059},[1011,1326,1278],{"class":1059},[861,1328,1329,1330,1332],{},"The command reads your current configuration, connects to the configured database, compiles both MMDB files, and exits. If ",[864,1331,1134],{}," is not found at the configured path, it prompts you to install it.",[1251,1334,1336],{"id":1335},"programmatic","Programmatic",[861,1338,1339,1340,1343],{},"Call ",[864,1341,1342],{},"runGeneration()"," from your application code. This is useful for triggering generation after a bulk ban operation.",[1001,1345,1347],{"className":1003,"code":1346,"filename":1005,"language":1006,"meta":1007,"style":1007},"import { runGeneration } from '@riavzon\u002Fbot-detector';\n\nawait runGeneration();\n",[864,1348,1349,1375,1380],{"__ignoreMap":1007},[1011,1350,1351,1354,1356,1359,1362,1365,1367,1370,1372],{"class":1013,"line":1014},[1011,1352,1353],{"class":1017},"import",[1011,1355,1040],{"class":1025},[1011,1357,1358],{"class":1032},"runGeneration",[1011,1360,1361],{"class":1025}," } ",[1011,1363,1364],{"class":1017},"from",[1011,1366,1056],{"class":1055},[1011,1368,1369],{"class":1059},"@riavzon\u002Fbot-detector",[1011,1371,1063],{"class":1055},[1011,1373,1374],{"class":1025},";\n",[1011,1376,1377],{"class":1013,"line":1029},[1011,1378,1379],{"emptyLinePlaceholder":8},"\n",[1011,1381,1382,1384,1387],{"class":1013,"line":1084},[1011,1383,1018],{"class":1017},[1011,1385,1386],{"class":1021}," runGeneration",[1011,1388,1389],{"class":1025},"();\n",[861,1391,1392,1394,1395,1398],{},[864,1393,1342],{}," requires ",[864,1396,1397],{},"defineConfiguration()"," to have been called and awaited first.",[884,1400],{},[887,1402,1404],{"id":1403},"scheduling-with-cron","Scheduling with Cron",[861,1406,1407],{},"Running generation on a schedule ensures that new bans from ongoing traffic are captured in the MMDB files regularly. The right frequency depends on your traffic volume.",[861,1409,1410],{},"The examples below use a standard Unix cron format. Adjust the path and user to match your deployment.",[1001,1412,1417],{"className":1413,"code":1414,"filename":1415,"language":1416,"meta":1007,"style":1007},"language-cron shiki shiki-themes light-plus light-plus dracula","# Run every night at 2:00 AM\n0 2 * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n","crontab","cron",[864,1418,1419,1424],{"__ignoreMap":1007},[1011,1420,1421],{"class":1013,"line":1014},[1011,1422,1423],{},"# Run every night at 2:00 AM\n",[1011,1425,1426],{"class":1013,"line":1029},[1011,1427,1428],{},"0 2 * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n",[861,1430,1431],{},"For higher-traffic applications where bans accumulate quickly:",[1001,1433,1435],{"className":1413,"code":1434,"filename":1415,"language":1416,"meta":1007,"style":1007},"# Run every hour\n0 * * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n",[864,1436,1437,1442],{"__ignoreMap":1007},[1011,1438,1439],{"class":1013,"line":1014},[1011,1440,1441],{},"# Run every hour\n",[1011,1443,1444],{"class":1013,"line":1029},[1011,1445,1446],{},"0 * * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n",[1448,1449,1450],"tip",{},[861,1451,1452],{},"Log the output of the generate command. It emits structured info lines including the entry count for each compiled database, making it easy to spot when traffic patterns change or ban volume spikes.",[1251,1454,1456],{"id":1455},"triggering-after-bulk-bans","Triggering After Bulk Bans",[861,1458,1459,1460,1462],{},"When you perform a bulk operation that adds many rows to the ",[864,1461,911],{}," table, for example, after processing a list of known bad IPs or running a manual review, trigger generation immediately rather than waiting for the next scheduled run:",[1001,1464,1467],{"className":1003,"code":1465,"filename":1466,"language":1006,"meta":1007,"style":1007},"import { updateBannedIP, runGeneration } from '@riavzon\u002Fbot-detector';\nimport type { BannedInfo } from '@riavzon\u002Fbot-detector';\n\n\u002F\u002F Bulk insert ban records...\nfor (const ip of badIps) {\n  const info: BannedInfo = { score: 100, reasons: ['PREVIOUSLY_BANNED_IP'] };\n  await updateBannedIP('', ip, 'us', '', info);\n}\n\n\u002F\u002F Compile updated MMDB immediately\nawait runGeneration();\n","admin-script.ts",[864,1468,1469,1494,1518,1522,1528,1553,1601,1641,1646,1650,1656],{"__ignoreMap":1007},[1011,1470,1471,1473,1475,1478,1480,1482,1484,1486,1488,1490,1492],{"class":1013,"line":1014},[1011,1472,1353],{"class":1017},[1011,1474,1040],{"class":1025},[1011,1476,1477],{"class":1032},"updateBannedIP",[1011,1479,1066],{"class":1025},[1011,1481,1358],{"class":1032},[1011,1483,1361],{"class":1025},[1011,1485,1364],{"class":1017},[1011,1487,1056],{"class":1055},[1011,1489,1369],{"class":1059},[1011,1491,1063],{"class":1055},[1011,1493,1374],{"class":1025},[1011,1495,1496,1498,1501,1503,1506,1508,1510,1512,1514,1516],{"class":1013,"line":1029},[1011,1497,1353],{"class":1017},[1011,1499,1500],{"class":1017}," type",[1011,1502,1040],{"class":1025},[1011,1504,1505],{"class":1032},"BannedInfo",[1011,1507,1361],{"class":1025},[1011,1509,1364],{"class":1017},[1011,1511,1056],{"class":1055},[1011,1513,1369],{"class":1059},[1011,1515,1063],{"class":1055},[1011,1517,1374],{"class":1025},[1011,1519,1520],{"class":1013,"line":1084},[1011,1521,1379],{"emptyLinePlaceholder":8},[1011,1523,1524],{"class":1013,"line":1095},[1011,1525,1527],{"class":1526},"sghk6","\u002F\u002F Bulk insert ban records...\n",[1011,1529,1530,1533,1536,1540,1544,1547,1550],{"class":1013,"line":1110},[1011,1531,1532],{"class":1017},"for",[1011,1534,1535],{"class":1025}," (",[1011,1537,1539],{"class":1538},"sl46w","const",[1011,1541,1543],{"class":1542},"s3JHE"," ip",[1011,1545,1546],{"class":1538}," of",[1011,1548,1549],{"class":1032}," badIps",[1011,1551,1552],{"class":1025},") {\n",[1011,1554,1555,1558,1561,1564,1568,1571,1573,1576,1578,1581,1583,1586,1588,1591,1593,1596,1598],{"class":1013,"line":1124},[1011,1556,1557],{"class":1538},"  const",[1011,1559,1560],{"class":1542}," info",[1011,1562,1037],{"class":1563},"saOXh",[1011,1565,1567],{"class":1566},"sFs1U"," BannedInfo",[1011,1569,1570],{"class":1563}," =",[1011,1572,1040],{"class":1025},[1011,1574,1575],{"class":1032},"score",[1011,1577,1037],{"class":1036},[1011,1579,1580],{"class":1103}," 100",[1011,1582,1066],{"class":1025},[1011,1584,1585],{"class":1032},"reasons",[1011,1587,1037],{"class":1036},[1011,1589,1590],{"class":1025}," [",[1011,1592,1063],{"class":1055},[1011,1594,1595],{"class":1059},"PREVIOUSLY_BANNED_IP",[1011,1597,1063],{"class":1055},[1011,1599,1600],{"class":1025},"] };\n",[1011,1602,1603,1606,1609,1612,1615,1617,1620,1622,1624,1627,1629,1631,1633,1635,1638],{"class":1013,"line":1141},[1011,1604,1605],{"class":1017},"  await",[1011,1607,1608],{"class":1021}," updateBannedIP",[1011,1610,1611],{"class":1025},"(",[1011,1613,1614],{"class":1055},"''",[1011,1616,1066],{"class":1025},[1011,1618,1619],{"class":1032},"ip",[1011,1621,1066],{"class":1025},[1011,1623,1063],{"class":1055},[1011,1625,1626],{"class":1059},"us",[1011,1628,1063],{"class":1055},[1011,1630,1066],{"class":1025},[1011,1632,1614],{"class":1055},[1011,1634,1066],{"class":1025},[1011,1636,1637],{"class":1032},"info",[1011,1639,1640],{"class":1025},");\n",[1011,1642,1643],{"class":1013,"line":1153},[1011,1644,1645],{"class":1025},"}\n",[1011,1647,1648],{"class":1013,"line":1159},[1011,1649,1379],{"emptyLinePlaceholder":8},[1011,1651,1653],{"class":1013,"line":1652},10,[1011,1654,1655],{"class":1526},"\u002F\u002F Compile updated MMDB immediately\n",[1011,1657,1659,1661,1663],{"class":1013,"line":1658},11,[1011,1660,1018],{"class":1017},[1011,1662,1386],{"class":1021},[1011,1664,1389],{"class":1025},[884,1666],{},[887,1668,1670,1672,1673,1675],{"id":1669},"scorethreshold-and-the-highriskmmdb-tradeoff",[864,1671,1169],{}," and the ",[864,1674,874],{}," Tradeoff",[861,1677,991,1678,1680,1681,1683],{},[864,1679,1169],{}," value controls how aggressively ",[864,1682,874],{}," is populated. A lower threshold includes visitors who triggered only a few weak signals. A higher threshold limits the file to visitors with strong evidence of suspicious behavior.",[1685,1686,1687,1700],"table",{},[1688,1689,1690],"thead",{},[1691,1692,1693,1697],"tr",{},[1694,1695,1696],"th",{},"Threshold",[1694,1698,1699],{},"Effect",[1701,1702,1703,1714,1724],"tbody",{},[1691,1704,1705,1711],{},[1706,1707,1708],"td",{},[864,1709,1710],{},"40",[1706,1712,1713],{},"Catches more visitors who have accumulated moderate scores. Higher risk of including false positives.",[1691,1715,1716,1721],{},[1706,1717,1718,1720],{},[864,1719,945],{}," (default)",[1706,1722,1723],{},"Balanced. Captures visitors with strong suspicious history.",[1691,1725,1726,1731],{},[1706,1727,1728],{},[864,1729,1730],{},"90",[1706,1732,1733],{},"Conservative. Only the most suspicious non-banned visitors are included.",[972,1735,1736],{},[861,1737,991,1738,1740,1741,1744,1745,1748,1749,1751,1752,1754,1755,1757],{},[864,1739,949],{}," configured under ",[864,1742,1743],{},"enableKnownBadIpsCheck"," controls how many points a high-risk IP receives on its next visit (default ",[864,1746,1747],{},"30","). This is separate from ",[864,1750,1169],{},". Raising ",[864,1753,949],{}," makes high-risk IPs easier to ban on return; lowering it uses ",[864,1756,874],{}," as a contributing signal rather than a near-ban trigger.",[1759,1760,1761],"style",{},"html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}html pre.shiki code .sDd4n, html code.shiki .sDd4n{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#F8F8F2}html pre.shiki code .sjsA6, html code.shiki .sjsA6{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#F8F8F2}html pre.shiki code .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}html pre.shiki code .sFkSl, html code.shiki .sFkSl{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#E9F284}html pre.shiki code .sFB1V, html code.shiki .sFB1V{--shiki-light:#A31515;--shiki-default:#A31515;--shiki-dark:#F1FA8C}html pre.shiki code .spgvN, html code.shiki .spgvN{--shiki-light:#098658;--shiki-default:#098658;--shiki-dark:#BD93F9}html pre.shiki code .sjR7W, html code.shiki .sjR7W{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#BD93F9}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}html pre.shiki code .sl46w, html code.shiki .sl46w{--shiki-light:#0000FF;--shiki-default:#0000FF;--shiki-dark:#FF79C6}html pre.shiki code .s3JHE, html code.shiki .s3JHE{--shiki-light:#0070C1;--shiki-default:#0070C1;--shiki-dark:#F8F8F2}html pre.shiki code .saOXh, html code.shiki .saOXh{--shiki-light:#000000;--shiki-default:#000000;--shiki-dark:#FF79C6}html pre.shiki code .sFs1U, html code.shiki .sFs1U{--shiki-light:#267F99;--shiki-light-font-style:inherit;--shiki-default:#267F99;--shiki-default-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic}",{"title":1007,"searchDepth":1029,"depth":1029,"links":1763},[1764,1765,1766,1767,1771,1774],{"id":889,"depth":1029,"text":890},{"id":962,"depth":1029,"text":963},{"id":988,"depth":1029,"text":213},{"id":1248,"depth":1029,"text":1249,"children":1768},[1769,1770],{"id":1253,"depth":1084,"text":79},{"id":1335,"depth":1084,"text":1336},{"id":1403,"depth":1029,"text":1404,"children":1772},[1773],{"id":1455,"depth":1084,"text":1456},{"id":1669,"depth":1029,"text":1775},"scoreThreshold and the highRisk.mmdb Tradeoff","How bot-detector generate works, what it produces, when to run it, and how to automate it.","md",{},null,"---\ntitle: Scheduling Database Generation\ndescription: How bot-detector generate works, what it produces, when to run it, and how to automate it.\nicon: i-lucide-database-zap\n---\n\n`bot-detector generate` reads your accumulated visitor and ban history and compiles it into two binary MMDB databases: `banned.mmdb` and `highRisk.mmdb`. Once compiled, the [Known Bad IPs](\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips) checker reads these files in the cheap phase, rejecting repeat offenders in microseconds without running the full 17-checker pipeline.\n\nThe databases do not exist on a fresh installation. They grow as your application accumulates traffic and ban records, and they only become useful once you run generation for the first time.\n\n---\n\n## What Generation Produces\n\nRunning `bot-detector generate` compiles two files into `_data-sources\u002F` inside the package directory. Both run in parallel.\n\n**`banned.mmdb`**: built from every row in the `banned` table that has a non-null `ip_address`. Each entry stores the IP, score, country, user agent, and reason codes from the ban event. The [Known Bad IPs](\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips) checker reads this file and triggers an immediate `BAD_BOT_DETECTED` ban for any matching IP, skipping all other checkers.\n\n**`highRisk.mmdb`**: built from rows in the `visitors` table where `suspicious_activity_score` is at or above `generator.scoreThreshold` (default `70`). These are visitors who accumulated a suspicious score but were never banned. On their next visit, they receive `highRiskPenalty` points in the cheap phase, bringing them closer to `banScore` with less effort from other checkers.\n\nIf either table has no qualifying rows, the corresponding file is skipped silently. The checker handles missing files by skipping that lookup.\n\n---\n\n## Hot Reload\n\nThe MMDB files are opened with `watchForUpdates: true`. When a new file is written to disk, the reader reloads it automatically without restarting your application. You can run generation while traffic is live and the updated databases take effect within seconds.\n\n::note\n`watchForUpdates` is disabled during tests (`NODE_ENV=test`). In all other environments it is always on.\n::\n\n---\n\n## Configuration\n\nThe `generator` block in `defineConfiguration` controls all generation behavior.\n\n```ts [server.ts]\nawait defineConfiguration({\n  store: { main: { driver: 'sqlite', name: '.\u002Fbot-detector.db' } },\n  generator: {\n    scoreThreshold: 70,\n    deleteAfterBuild: false,\n    mmdbctlPath: 'mmdbctl',\n    generateTypes: false,\n  },\n})\n```\n\n::field-group\n::field{name=\"scoreThreshold\" type=\"number\"}\nMinimum `suspicious_activity_score` for a visitor row to be included in `highRisk.mmdb`. Lower values include more visitors; higher values are more conservative. Default: `70`.\n::\n\n::field{name=\"deleteAfterBuild\" type=\"boolean\"}\nWhen `true`, rows compiled into `banned.mmdb` are deleted from the `banned` table, and high-risk rows compiled into `highRisk.mmdb` are deleted from the `visitors` table. This keeps the database lean but means the raw rows are gone after generation. Default: `false`.\n::\n\n::field{name=\"mmdbctlPath\" type=\"string\"}\nPath to the `mmdbctl` binary used to compile MMDB files. If the binary is not on `PATH`, provide the absolute path here. Default: `'mmdbctl'`.\n::\n\n::field{name=\"generateTypes\" type=\"boolean\"}\nWhen `true`, TypeScript type definitions are generated alongside the MMDB files. Useful during development. Default: `false`.\n::\n::\n\n::warning\n`deleteAfterBuild: true` permanently removes rows from your database after each generation. If generation fails mid-run, some rows may already be deleted. Only enable this if your database size is a concern and you have another record of your ban history.\n::\n\n---\n\n## Running Generation\n\n### CLI\n\nRun generation from the terminal at any time:\n\n::code-group\n\n```bash [npm]\nnpx @riavzon\u002Fbot-detector generate\n```\n\n```bash [pnpm]\npnpm dlx @riavzon\u002Fbot-detector generate\n```\n\n```bash [yarn]\nyarn dlx @riavzon\u002Fbot-detector generate\n```\n\n\n```bash [bun]\nbunx @riavzon\u002Fbot-detector generate\n```\n::\n\nThe command reads your current configuration, connects to the configured database, compiles both MMDB files, and exits. If `mmdbctl` is not found at the configured path, it prompts you to install it.\n\n### Programmatic\n\nCall `runGeneration()` from your application code. This is useful for triggering generation after a bulk ban operation.\n\n```ts [server.ts]\nimport { runGeneration } from '@riavzon\u002Fbot-detector';\n\nawait runGeneration();\n```\n\n`runGeneration()` requires `defineConfiguration()` to have been called and awaited first.\n\n---\n\n## Scheduling with Cron\n\nRunning generation on a schedule ensures that new bans from ongoing traffic are captured in the MMDB files regularly. The right frequency depends on your traffic volume.\n\nThe examples below use a standard Unix cron format. Adjust the path and user to match your deployment.\n\n```cron [crontab]\n# Run every night at 2:00 AM\n0 2 * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n```\n\nFor higher-traffic applications where bans accumulate quickly:\n\n```cron [crontab]\n# Run every hour\n0 * * * * cd \u002Fapp && npx bot-detector generate >> \u002Fvar\u002Flog\u002Fbot-detector-generate.log 2>&1\n```\n\n::tip\nLog the output of the generate command. It emits structured info lines including the entry count for each compiled database, making it easy to spot when traffic patterns change or ban volume spikes.\n::\n\n### Triggering After Bulk Bans\n\nWhen you perform a bulk operation that adds many rows to the `banned` table, for example, after processing a list of known bad IPs or running a manual review, trigger generation immediately rather than waiting for the next scheduled run:\n\n```ts [admin-script.ts]\nimport { updateBannedIP, runGeneration } from '@riavzon\u002Fbot-detector';\nimport type { BannedInfo } from '@riavzon\u002Fbot-detector';\n\n\u002F\u002F Bulk insert ban records...\nfor (const ip of badIps) {\n  const info: BannedInfo = { score: 100, reasons: ['PREVIOUSLY_BANNED_IP'] };\n  await updateBannedIP('', ip, 'us', '', info);\n}\n\n\u002F\u002F Compile updated MMDB immediately\nawait runGeneration();\n```\n\n---\n\n## `scoreThreshold` and the `highRisk.mmdb` Tradeoff\n\nThe `scoreThreshold` value controls how aggressively `highRisk.mmdb` is populated. A lower threshold includes visitors who triggered only a few weak signals. A higher threshold limits the file to visitors with strong evidence of suspicious behavior.\n\n| Threshold | Effect |\n| --- | --- |\n| `40` | Catches more visitors who have accumulated moderate scores. Higher risk of including false positives. |\n| `70` (default) | Balanced. Captures visitors with strong suspicious history. |\n| `90` | Conservative. Only the most suspicious non-banned visitors are included. |\n\n::note\nThe `highRiskPenalty` configured under `enableKnownBadIpsCheck` controls how many points a high-risk IP receives on its next visit (default `30`). This is separate from `scoreThreshold`. Raising `highRiskPenalty` makes high-risk IPs easier to ban on return; lowering it uses `highRisk.mmdb` as a contributing signal rather than a near-ban trigger.\n::\n",{"title":99,"description":1776},"IutI-6vEK9ekKopUVSDxXx8gNc6wfNiPQYbttRaJCdg",[1784,1785],{"title":94,"path":95,"stem":96,"children":-1},{"title":103,"path":104,"stem":105,"children":-1},{"id":856,"title":99,"body":1787,"description":1776,"extension":1777,"icon":41,"meta":2434,"module":1779,"navigation":8,"path":100,"rawbody":1780,"seo":2435,"stem":101,"__hash__":1782},{"type":858,"value":1788,"toc":2421},[1789,1799,1801,1803,1805,1811,1825,1843,1845,1847,1849,1853,1861,1863,1865,1871,1981,2027,2033,2035,2037,2039,2041,2095,2099,2101,2105,2141,2147,2149,2151,2153,2155,2167,2169,2181,2185,2187,2191,2353,2355,2361,2367,2403,2419],[861,1790,1791,867,1793,871,1795,875,1797,879],{},[864,1792,866],{},[864,1794,870],{},[864,1796,874],{},[877,1798,164],{"href":165},[861,1800,882],{},[884,1802],{},[887,1804,890],{"id":889},[861,1806,893,1807,896,1809,900],{},[864,1808,866],{},[864,1810,899],{},[861,1812,1813,908,1817,912,1819,916,1821,919,1823,923],{},[904,1814,1815],{},[864,1816,870],{},[864,1818,911],{},[864,1820,915],{},[877,1822,164],{"href":165},[864,1824,922],{},[861,1826,1827,930,1831,934,1833,938,1835,942,1837,946,1839,950,1841,954],{},[904,1828,1829],{},[864,1830,874],{},[864,1832,933],{},[864,1834,937],{},[864,1836,941],{},[864,1838,945],{},[864,1840,949],{},[864,1842,953],{},[861,1844,957],{},[884,1846],{},[887,1848,963],{"id":962},[861,1850,966,1851,970],{},[864,1852,969],{},[972,1854,1855],{},[861,1856,1857,979,1859,983],{},[864,1858,978],{},[864,1860,982],{},[884,1862],{},[887,1864,213],{"id":988},[861,1866,991,1867,995,1869,999],{},[864,1868,994],{},[864,1870,998],{},[1001,1872,1873],{"className":1003,"code":1004,"filename":1005,"language":1006,"meta":1007,"style":1007},[864,1874,1875,1883,1921,1929,1939,1949,1963,1973,1977],{"__ignoreMap":1007},[1011,1876,1877,1879,1881],{"class":1013,"line":1014},[1011,1878,1018],{"class":1017},[1011,1880,1022],{"class":1021},[1011,1882,1026],{"class":1025},[1011,1884,1885,1887,1889,1891,1893,1895,1897,1899,1901,1903,1905,1907,1909,1911,1913,1915,1917,1919],{"class":1013,"line":1029},[1011,1886,1033],{"class":1032},[1011,1888,1037],{"class":1036},[1011,1890,1040],{"class":1025},[1011,1892,1043],{"class":1032},[1011,1894,1037],{"class":1036},[1011,1896,1040],{"class":1025},[1011,1898,1050],{"class":1032},[1011,1900,1037],{"class":1036},[1011,1902,1056],{"class":1055},[1011,1904,1060],{"class":1059},[1011,1906,1063],{"class":1055},[1011,1908,1066],{"class":1025},[1011,1910,1069],{"class":1032},[1011,1912,1037],{"class":1036},[1011,1914,1056],{"class":1055},[1011,1916,1076],{"class":1059},[1011,1918,1063],{"class":1055},[1011,1920,1081],{"class":1025},[1011,1922,1923,1925,1927],{"class":1013,"line":1084},[1011,1924,1087],{"class":1032},[1011,1926,1037],{"class":1036},[1011,1928,1092],{"class":1025},[1011,1930,1931,1933,1935,1937],{"class":1013,"line":1095},[1011,1932,1098],{"class":1032},[1011,1934,1037],{"class":1036},[1011,1936,1104],{"class":1103},[1011,1938,1107],{"class":1025},[1011,1940,1941,1943,1945,1947],{"class":1013,"line":1110},[1011,1942,1113],{"class":1032},[1011,1944,1037],{"class":1036},[1011,1946,1119],{"class":1118},[1011,1948,1107],{"class":1025},[1011,1950,1951,1953,1955,1957,1959,1961],{"class":1013,"line":1124},[1011,1952,1127],{"class":1032},[1011,1954,1037],{"class":1036},[1011,1956,1056],{"class":1055},[1011,1958,1134],{"class":1059},[1011,1960,1063],{"class":1055},[1011,1962,1107],{"class":1025},[1011,1964,1965,1967,1969,1971],{"class":1013,"line":1141},[1011,1966,1144],{"class":1032},[1011,1968,1037],{"class":1036},[1011,1970,1119],{"class":1118},[1011,1972,1107],{"class":1025},[1011,1974,1975],{"class":1013,"line":1153},[1011,1976,1156],{"class":1025},[1011,1978,1979],{"class":1013,"line":1159},[1011,1980,1162],{"class":1025},[1164,1982,1983,1993,2009,2019],{},[1167,1984,1985],{"name":1169,"type":1170},[861,1986,1173,1987,1176,1989,1179,1991,1182],{},[864,1988,937],{},[864,1990,874],{},[864,1992,945],{},[1167,1994,1995],{"name":1185,"type":1186},[861,1996,1189,1997,1193,1999,1196,2001,1199,2003,1196,2005,1204,2007,1182],{},[864,1998,1192],{},[864,2000,870],{},[864,2002,911],{},[864,2004,874],{},[864,2006,933],{},[864,2008,1207],{},[1167,2010,2011],{"name":1210,"type":1211},[861,2012,1214,2013,1217,2015,1221,2017,1182],{},[864,2014,1134],{},[864,2016,1220],{},[864,2018,1224],{},[1167,2020,2021],{"name":1227,"type":1186},[861,2022,1189,2023,1232,2025,1182],{},[864,2024,1192],{},[864,2026,1207],{},[1236,2028,2029],{},[861,2030,2031,1243],{},[864,2032,1242],{},[884,2034],{},[887,2036,1249],{"id":1248},[1251,2038,79],{"id":1253},[861,2040,1256],{},[1258,2042,2043,2055,2069,2083],{},[1001,2044,2045],{"className":1262,"code":1263,"filename":1264,"language":1265,"meta":1007,"style":1007},[864,2046,2047],{"__ignoreMap":1007},[1011,2048,2049,2051,2053],{"class":1013,"line":1014},[1011,2050,1272],{"class":1021},[1011,2052,1275],{"class":1059},[1011,2054,1278],{"class":1059},[1001,2056,2057],{"className":1262,"code":1281,"filename":1282,"language":1265,"meta":1007,"style":1007},[864,2058,2059],{"__ignoreMap":1007},[1011,2060,2061,2063,2065,2067],{"class":1013,"line":1014},[1011,2062,1282],{"class":1021},[1011,2064,1291],{"class":1059},[1011,2066,1275],{"class":1059},[1011,2068,1278],{"class":1059},[1001,2070,2071],{"className":1262,"code":1298,"filename":1299,"language":1265,"meta":1007,"style":1007},[864,2072,2073],{"__ignoreMap":1007},[1011,2074,2075,2077,2079,2081],{"class":1013,"line":1014},[1011,2076,1299],{"class":1021},[1011,2078,1291],{"class":1059},[1011,2080,1275],{"class":1059},[1011,2082,1278],{"class":1059},[1001,2084,2085],{"className":1262,"code":1314,"filename":1315,"language":1265,"meta":1007,"style":1007},[864,2086,2087],{"__ignoreMap":1007},[1011,2088,2089,2091,2093],{"class":1013,"line":1014},[1011,2090,1322],{"class":1021},[1011,2092,1275],{"class":1059},[1011,2094,1278],{"class":1059},[861,2096,1329,2097,1332],{},[864,2098,1134],{},[1251,2100,1336],{"id":1335},[861,2102,1339,2103,1343],{},[864,2104,1342],{},[1001,2106,2107],{"className":1003,"code":1346,"filename":1005,"language":1006,"meta":1007,"style":1007},[864,2108,2109,2129,2133],{"__ignoreMap":1007},[1011,2110,2111,2113,2115,2117,2119,2121,2123,2125,2127],{"class":1013,"line":1014},[1011,2112,1353],{"class":1017},[1011,2114,1040],{"class":1025},[1011,2116,1358],{"class":1032},[1011,2118,1361],{"class":1025},[1011,2120,1364],{"class":1017},[1011,2122,1056],{"class":1055},[1011,2124,1369],{"class":1059},[1011,2126,1063],{"class":1055},[1011,2128,1374],{"class":1025},[1011,2130,2131],{"class":1013,"line":1029},[1011,2132,1379],{"emptyLinePlaceholder":8},[1011,2134,2135,2137,2139],{"class":1013,"line":1084},[1011,2136,1018],{"class":1017},[1011,2138,1386],{"class":1021},[1011,2140,1389],{"class":1025},[861,2142,2143,1394,2145,1398],{},[864,2144,1342],{},[864,2146,1397],{},[884,2148],{},[887,2150,1404],{"id":1403},[861,2152,1407],{},[861,2154,1410],{},[1001,2156,2157],{"className":1413,"code":1414,"filename":1415,"language":1416,"meta":1007,"style":1007},[864,2158,2159,2163],{"__ignoreMap":1007},[1011,2160,2161],{"class":1013,"line":1014},[1011,2162,1423],{},[1011,2164,2165],{"class":1013,"line":1029},[1011,2166,1428],{},[861,2168,1431],{},[1001,2170,2171],{"className":1413,"code":1434,"filename":1415,"language":1416,"meta":1007,"style":1007},[864,2172,2173,2177],{"__ignoreMap":1007},[1011,2174,2175],{"class":1013,"line":1014},[1011,2176,1441],{},[1011,2178,2179],{"class":1013,"line":1029},[1011,2180,1446],{},[1448,2182,2183],{},[861,2184,1452],{},[1251,2186,1456],{"id":1455},[861,2188,1459,2189,1462],{},[864,2190,911],{},[1001,2192,2193],{"className":1003,"code":1465,"filename":1466,"language":1006,"meta":1007,"style":1007},[864,2194,2195,2219,2241,2245,2249,2265,2301,2333,2337,2341,2345],{"__ignoreMap":1007},[1011,2196,2197,2199,2201,2203,2205,2207,2209,2211,2213,2215,2217],{"class":1013,"line":1014},[1011,2198,1353],{"class":1017},[1011,2200,1040],{"class":1025},[1011,2202,1477],{"class":1032},[1011,2204,1066],{"class":1025},[1011,2206,1358],{"class":1032},[1011,2208,1361],{"class":1025},[1011,2210,1364],{"class":1017},[1011,2212,1056],{"class":1055},[1011,2214,1369],{"class":1059},[1011,2216,1063],{"class":1055},[1011,2218,1374],{"class":1025},[1011,2220,2221,2223,2225,2227,2229,2231,2233,2235,2237,2239],{"class":1013,"line":1029},[1011,2222,1353],{"class":1017},[1011,2224,1500],{"class":1017},[1011,2226,1040],{"class":1025},[1011,2228,1505],{"class":1032},[1011,2230,1361],{"class":1025},[1011,2232,1364],{"class":1017},[1011,2234,1056],{"class":1055},[1011,2236,1369],{"class":1059},[1011,2238,1063],{"class":1055},[1011,2240,1374],{"class":1025},[1011,2242,2243],{"class":1013,"line":1084},[1011,2244,1379],{"emptyLinePlaceholder":8},[1011,2246,2247],{"class":1013,"line":1095},[1011,2248,1527],{"class":1526},[1011,2250,2251,2253,2255,2257,2259,2261,2263],{"class":1013,"line":1110},[1011,2252,1532],{"class":1017},[1011,2254,1535],{"class":1025},[1011,2256,1539],{"class":1538},[1011,2258,1543],{"class":1542},[1011,2260,1546],{"class":1538},[1011,2262,1549],{"class":1032},[1011,2264,1552],{"class":1025},[1011,2266,2267,2269,2271,2273,2275,2277,2279,2281,2283,2285,2287,2289,2291,2293,2295,2297,2299],{"class":1013,"line":1124},[1011,2268,1557],{"class":1538},[1011,2270,1560],{"class":1542},[1011,2272,1037],{"class":1563},[1011,2274,1567],{"class":1566},[1011,2276,1570],{"class":1563},[1011,2278,1040],{"class":1025},[1011,2280,1575],{"class":1032},[1011,2282,1037],{"class":1036},[1011,2284,1580],{"class":1103},[1011,2286,1066],{"class":1025},[1011,2288,1585],{"class":1032},[1011,2290,1037],{"class":1036},[1011,2292,1590],{"class":1025},[1011,2294,1063],{"class":1055},[1011,2296,1595],{"class":1059},[1011,2298,1063],{"class":1055},[1011,2300,1600],{"class":1025},[1011,2302,2303,2305,2307,2309,2311,2313,2315,2317,2319,2321,2323,2325,2327,2329,2331],{"class":1013,"line":1141},[1011,2304,1605],{"class":1017},[1011,2306,1608],{"class":1021},[1011,2308,1611],{"class":1025},[1011,2310,1614],{"class":1055},[1011,2312,1066],{"class":1025},[1011,2314,1619],{"class":1032},[1011,2316,1066],{"class":1025},[1011,2318,1063],{"class":1055},[1011,2320,1626],{"class":1059},[1011,2322,1063],{"class":1055},[1011,2324,1066],{"class":1025},[1011,2326,1614],{"class":1055},[1011,2328,1066],{"class":1025},[1011,2330,1637],{"class":1032},[1011,2332,1640],{"class":1025},[1011,2334,2335],{"class":1013,"line":1153},[1011,2336,1645],{"class":1025},[1011,2338,2339],{"class":1013,"line":1159},[1011,2340,1379],{"emptyLinePlaceholder":8},[1011,2342,2343],{"class":1013,"line":1652},[1011,2344,1655],{"class":1526},[1011,2346,2347,2349,2351],{"class":1013,"line":1658},[1011,2348,1018],{"class":1017},[1011,2350,1386],{"class":1021},[1011,2352,1389],{"class":1025},[884,2354],{},[887,2356,2357,1672,2359,1675],{"id":1669},[864,2358,1169],{},[864,2360,874],{},[861,2362,991,2363,1680,2365,1683],{},[864,2364,1169],{},[864,2366,874],{},[1685,2368,2369,2377],{},[1688,2370,2371],{},[1691,2372,2373,2375],{},[1694,2374,1696],{},[1694,2376,1699],{},[1701,2378,2379,2387,2395],{},[1691,2380,2381,2385],{},[1706,2382,2383],{},[864,2384,1710],{},[1706,2386,1713],{},[1691,2388,2389,2393],{},[1706,2390,2391,1720],{},[864,2392,945],{},[1706,2394,1723],{},[1691,2396,2397,2401],{},[1706,2398,2399],{},[864,2400,1730],{},[1706,2402,1733],{},[972,2404,2405],{},[861,2406,991,2407,1740,2409,1744,2411,1748,2413,1751,2415,1754,2417,1757],{},[864,2408,949],{},[864,2410,1743],{},[864,2412,1747],{},[864,2414,1169],{},[864,2416,949],{},[864,2418,874],{},[1759,2420,1761],{},{"title":1007,"searchDepth":1029,"depth":1029,"links":2422},[2423,2424,2425,2426,2430,2433],{"id":889,"depth":1029,"text":890},{"id":962,"depth":1029,"text":963},{"id":988,"depth":1029,"text":213},{"id":1248,"depth":1029,"text":1249,"children":2427},[2428,2429],{"id":1253,"depth":1084,"text":79},{"id":1335,"depth":1084,"text":1336},{"id":1403,"depth":1029,"text":1404,"children":2431},[2432],{"id":1455,"depth":1084,"text":1456},{"id":1669,"depth":1029,"text":1775},{},{"title":99,"description":1776},1780564517588]