[{"data":1,"prerenderedAt":7216},["ShallowReactive",2],{"navLinks":3,"sidebar_docs_navigation_\u002Fdocs\u002Fiam":64,"navigation":257,"navLinks_footer":837,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice_page":850,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice_surround":4444,"\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice":4447},{"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":27,"path":29,"stem":70,"children":71},"docs\u002Fiam\u002Findex",[72,73,76,216,219,236,240],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},"\u002Fdocs\u002Fiam\u002Fgetting-started","docs\u002Fiam\u002F00.getting-started",{"title":77,"path":78,"stem":79,"children":80},"Essentials","\u002Fdocs\u002Fiam\u002Fessentials","docs\u002Fiam\u002F01.essentials\u002Findex",[81,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},"Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Ftokens","docs\u002Fiam\u002F01.essentials\u002F00.tokens",{"title":87,"path":88,"stem":89},"Access Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Faccess-tokens","docs\u002Fiam\u002F01.essentials\u002F01.access-tokens",{"title":91,"path":92,"stem":93},"Refresh Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens","docs\u002Fiam\u002F01.essentials\u002F02.refresh-tokens",{"title":95,"path":96,"stem":97},"Anomaly Detection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies","docs\u002Fiam\u002F01.essentials\u002F03.anomalies",{"title":99,"path":100,"stem":101},"Signup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup","docs\u002Fiam\u002F01.essentials\u002F04.signup",{"title":103,"path":104,"stem":105},"Login","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin","docs\u002Fiam\u002F01.essentials\u002F05.login",{"title":107,"path":108,"stem":109},"Logout","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout","docs\u002Fiam\u002F01.essentials\u002F06.logout",{"title":111,"path":112,"stem":113},"OAuth","\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth","docs\u002Fiam\u002F01.essentials\u002F07.oauth",{"title":115,"path":116,"stem":117},"Magic Links","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmagic-links","docs\u002Fiam\u002F01.essentials\u002F08.magic-links",{"title":119,"path":120,"stem":121},"Emails","\u002Fdocs\u002Fiam\u002Fessentials\u002Femails","docs\u002Fiam\u002F01.essentials\u002F09.emails",{"title":123,"path":124,"stem":125},"MFA","\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa","docs\u002Fiam\u002F01.essentials\u002F10.mfa",{"title":127,"path":128,"stem":129},"Fingerprinting","\u002Fdocs\u002Fiam\u002Fessentials\u002Ffingerprinting","docs\u002Fiam\u002F01.essentials\u002F11.fingerprinting",{"title":131,"path":132,"stem":133},"Backend for Frontend","\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff","docs\u002Fiam\u002F01.essentials\u002F12.bff",{"title":135,"path":136,"stem":137},"HMAC Authentication","\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac","docs\u002Fiam\u002F01.essentials\u002F13.hmac",{"title":139,"path":140,"stem":141},"XSS Protection","\u002Fdocs\u002Fiam\u002Fessentials\u002Fxss","docs\u002Fiam\u002F01.essentials\u002F14.xss",{"title":143,"path":144,"stem":145},"Logging","\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging","docs\u002Fiam\u002F01.essentials\u002F15.logging",{"title":147,"path":148,"stem":149},"Rate Limiting","\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F16.rate-limiting",{"title":151,"path":152,"stem":153},"Database","\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase","docs\u002Fiam\u002F01.essentials\u002F17.database",{"title":155,"path":156,"stem":157},"Cookies","\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies","docs\u002Fiam\u002F01.essentials\u002F18.cookies",{"title":159,"path":160,"stem":161},"Service Startup","\u002Fdocs\u002Fiam\u002Fessentials\u002Fservice","docs\u002Fiam\u002F01.essentials\u002F19.service",{"title":163,"path":164,"stem":165},"Password Reset","\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset","docs\u002Fiam\u002F01.essentials\u002F20.password-reset",{"title":167,"path":168,"stem":169,"children":170},"API Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi","docs\u002Fiam\u002F01.essentials\u002F21.api\u002Findex",[171,172,176,180,210,213],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},"Creating Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F00.creation",{"title":177,"path":178,"stem":179},"Verifying Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F01.verification",{"title":181,"path":182,"stem":183,"children":184},"Manage Tokens","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002Findex",[185,186,190,194,198,202,206],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},"Privileges","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F00.privilege",{"title":191,"path":192,"stem":193},"Revocation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F01.revocation",{"title":195,"path":196,"stem":197},"Rotation","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F02.rotation",{"title":199,"path":200,"stem":201},"IP Restriction","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F03.ip-updates",{"title":203,"path":204,"stem":205},"Metadata","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F04.metadata",{"title":207,"path":208,"stem":209},"Token Listing","\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F02.management\u002F05.list",{"title":147,"path":211,"stem":212},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Frate-limiting","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F03.rate-limiting",{"title":38,"path":214,"stem":215},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fsecurity","docs\u002Fiam\u002F01.essentials\u002F21.api\u002F04.security",{"title":38,"path":217,"stem":218},"\u002Fdocs\u002Fiam\u002Fsecurity","docs\u002Fiam\u002F02.security",{"title":220,"path":221,"stem":222,"children":223,"page":53},"Guides","\u002Fdocs\u002Fiam\u002Fguides","docs\u002Fiam\u002F03.guides",[224,228,232],{"title":225,"path":226,"stem":227},"Deployment","\u002Fdocs\u002Fiam\u002Fguides\u002Fdeployment","docs\u002Fiam\u002F03.guides\u002Fdeployment",{"title":229,"path":230,"stem":231},"Operation Scripts","\u002Fdocs\u002Fiam\u002Fguides\u002Foperation-scripts","docs\u002Fiam\u002F03.guides\u002Foperation-scripts",{"title":233,"path":234,"stem":235},"Role-Based Access Control","\u002Fdocs\u002Fiam\u002Fguides\u002Frbac","docs\u002Fiam\u002F03.guides\u002Frbac",{"title":237,"path":238,"stem":239},"Configuration","\u002Fdocs\u002Fiam\u002Fconfiguration","docs\u002Fiam\u002F04.configuration",{"title":241,"path":242,"stem":243,"children":244,"page":53},"Api","\u002Fdocs\u002Fiam\u002Fapi","docs\u002Fiam\u002F05.API",[245,249,253],{"title":246,"path":247,"stem":248},"API Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fapi","docs\u002Fiam\u002F05.API\u002F00.api",{"title":250,"path":251,"stem":252},"Middleware Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Fmiddlewares","docs\u002Fiam\u002F05.API\u002F02.middlewares",{"title":254,"path":255,"stem":256},"Routes Reference","\u002Fdocs\u002Fiam\u002Fapi\u002Froutes","docs\u002Fiam\u002F05.API\u002F03.routes",[258],{"title":9,"path":66,"stem":67,"children":259,"page":53},[260,398,516,521,577,644],{"title":20,"path":22,"stem":261,"children":262},"docs\u002Fauth-h3client\u002Findex",[263,264,273,307,331,353,356,376,379],{"title":20,"path":22,"stem":261},{"title":14,"path":265,"stem":266,"children":267},"\u002Fdocs\u002Fauth-h3client\u002Fgetting-started","docs\u002Fauth-h3client\u002F00.getting-started\u002Findex",[268,269],{"title":14,"path":265,"stem":266},{"title":270,"path":271,"stem":272},"Nuxt Module","\u002Fdocs\u002Fauth-h3client\u002Fgetting-started\u002Fnuxt","docs\u002Fauth-h3client\u002F00.getting-started\u002F00.nuxt",{"title":77,"path":274,"stem":275,"children":276},"\u002Fdocs\u002Fauth-h3client\u002Fessentials","docs\u002Fauth-h3client\u002F01.essentials\u002Findex",[277,278,282,286,290,294,298,301,304],{"title":77,"path":274,"stem":275},{"title":279,"path":280,"stem":281},"Session Management","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fsession","docs\u002Fauth-h3client\u002F01.essentials\u002F00.session",{"title":283,"path":284,"stem":285},"Route Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Froute-protection","docs\u002Fauth-h3client\u002F01.essentials\u002F01.route-protection",{"title":287,"path":288,"stem":289},"CSRF Protection","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcsrf","docs\u002Fauth-h3client\u002F01.essentials\u002F02.csrf",{"title":291,"path":292,"stem":293},"Auth Flows","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fauth-flows","docs\u002Fauth-h3client\u002F01.essentials\u002F03.auth-flows",{"title":295,"path":296,"stem":297},"OAuth and OIDC","\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Foauth","docs\u002Fauth-h3client\u002F01.essentials\u002F04.oauth",{"title":33,"path":299,"stem":300},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fbot-detection","docs\u002Fauth-h3client\u002F01.essentials\u002F05.bot-detection",{"title":155,"path":302,"stem":303},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Fcookies","docs\u002Fauth-h3client\u002F01.essentials\u002F06.cookies",{"title":143,"path":305,"stem":306},"\u002Fdocs\u002Fauth-h3client\u002Fessentials\u002Flogging","docs\u002Fauth-h3client\u002F01.essentials\u002F07.logging",{"title":123,"path":308,"stem":309,"children":310},"\u002Fdocs\u002Fauth-h3client\u002Fmfa","docs\u002Fauth-h3client\u002F02.mfa\u002Findex",[311,312,316,319,323,327],{"title":123,"path":308,"stem":309},{"title":313,"path":314,"stem":315},"Built-in MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fbuilt-in-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F01.built-in-flow",{"title":163,"path":317,"stem":318},"\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fpassword-reset","docs\u002Fauth-h3client\u002F02.mfa\u002F02.password-reset",{"title":320,"path":321,"stem":322},"Email Change","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Femail-change","docs\u002Fauth-h3client\u002F02.mfa\u002F03.email-change",{"title":324,"path":325,"stem":326},"Custom MFA Flow","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fcustom-flow","docs\u002Fauth-h3client\u002F02.mfa\u002F04.custom-flow",{"title":328,"path":329,"stem":330},"Client-Side MFA","\u002Fdocs\u002Fauth-h3client\u002Fmfa\u002Fclient-side","docs\u002Fauth-h3client\u002F02.mfa\u002F05.client-side",{"title":332,"path":333,"stem":334,"children":335},"Client-side","\u002Fdocs\u002Fauth-h3client\u002Fclient","docs\u002Fauth-h3client\u002F03.client\u002Findex",[336,337,341,345,349],{"title":332,"path":333,"stem":334},{"title":338,"path":339,"stem":340},"useAuthData","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-auth-data","docs\u002Fauth-h3client\u002F03.client\u002F00.use-auth-data",{"title":342,"path":343,"stem":344},"useMagicLink","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fuse-magic-link","docs\u002Fauth-h3client\u002F03.client\u002F01.use-magic-link",{"title":346,"path":347,"stem":348},"executeRequest","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fexecute-request","docs\u002Fauth-h3client\u002F03.client\u002F02.execute-request",{"title":350,"path":351,"stem":352},"getCsrfToken","\u002Fdocs\u002Fauth-h3client\u002Fclient\u002Fget-csrf-token","docs\u002Fauth-h3client\u002F03.client\u002F03.get-csrf-token",{"title":38,"path":354,"stem":355},"\u002Fdocs\u002Fauth-h3client\u002Fsecurity","docs\u002Fauth-h3client\u002F04.security",{"title":220,"path":357,"stem":358,"children":359,"page":53},"\u002Fdocs\u002Fauth-h3client\u002Fguides","docs\u002Fauth-h3client\u002F05.guides",[360,364,368,372],{"title":361,"path":362,"stem":363},"H3 and Nitro Setup","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fh3-nitro","docs\u002Fauth-h3client\u002F05.guides\u002F00.h3-nitro",{"title":365,"path":366,"stem":367},"HMAC Inter-service Auth","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fhmac","docs\u002Fauth-h3client\u002F05.guides\u002Fhmac",{"title":369,"path":370,"stem":371},"Image Upload","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fimage-upload","docs\u002Fauth-h3client\u002F05.guides\u002Fimage-upload",{"title":373,"path":374,"stem":375},"mTLS Configuration","\u002Fdocs\u002Fauth-h3client\u002Fguides\u002Fmtls","docs\u002Fauth-h3client\u002F05.guides\u002Fmtls",{"title":237,"path":377,"stem":378},"\u002Fdocs\u002Fauth-h3client\u002Fconfiguration","docs\u002Fauth-h3client\u002F06.configuration",{"title":246,"path":380,"stem":381,"children":382},"\u002Fdocs\u002Fauth-h3client\u002Fapi","docs\u002Fauth-h3client\u002F07.api\u002Findex",[383,384,387,390,394],{"title":246,"path":380,"stem":381},{"title":254,"path":385,"stem":386},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcontrollers","docs\u002Fauth-h3client\u002F07.api\u002F00.controllers",{"title":250,"path":388,"stem":389},"\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fmiddleware","docs\u002Fauth-h3client\u002F07.api\u002F01.middleware",{"title":391,"path":392,"stem":393},"Client-side Reference","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Fcomposables","docs\u002Fauth-h3client\u002F07.api\u002F02.composables",{"title":395,"path":396,"stem":397},"Utilities","\u002Fdocs\u002Fauth-h3client\u002Fapi\u002Futilities","docs\u002Fauth-h3client\u002F07.api\u002F03.utilities",{"title":399,"path":35,"stem":400,"children":401},"Bot Detector","docs\u002Fbot-detection\u002Findex",[402,403,406,410,414,433,507,510,513],{"title":399,"path":35,"stem":400},{"title":14,"path":404,"stem":405},"\u002Fdocs\u002Fbot-detection\u002Fgetting-started","docs\u002Fbot-detection\u002F00.getting-started",{"title":407,"path":408,"stem":409},"CLI","\u002Fdocs\u002Fbot-detection\u002Fcli","docs\u002Fbot-detection\u002F01.cli",{"title":411,"path":412,"stem":413},"Data Sources","\u002Fdocs\u002Fbot-detection\u002Fdata-sources","docs\u002Fbot-detection\u002F02.data-sources",{"title":220,"path":415,"stem":416,"children":417,"page":53},"\u002Fdocs\u002Fbot-detection\u002Fguides","docs\u002Fbot-detection\u002F03.guides",[418,422,426,429],{"title":419,"path":420,"stem":421},"Custom Checkers","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fcustom","docs\u002Fbot-detection\u002F03.guides\u002FCUSTOM",{"title":423,"path":424,"stem":425},"Scheduling Database Generation","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fgenerate","docs\u002Fbot-detection\u002F03.guides\u002FGENERATE",{"title":143,"path":427,"stem":428},"\u002Fdocs\u002Fbot-detection\u002Fguides\u002Flogging","docs\u002Fbot-detection\u002F03.guides\u002FLOGGING",{"title":430,"path":431,"stem":432},"Score Modes and Reputation Healing","\u002Fdocs\u002Fbot-detection\u002Fguides\u002Fscore","docs\u002Fbot-detection\u002F03.guides\u002FSCORE",{"title":434,"path":435,"stem":436,"children":437},"Checkers","\u002Fdocs\u002Fbot-detection\u002Fcheckers","docs\u002Fbot-detection\u002F04.checkers\u002Findex",[438,439,443,447,451,455,459,463,467,471,475,479,483,487,491,495,499,503],{"title":434,"path":435,"stem":436},{"title":440,"path":441,"stem":442},"IP Validation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fip-validation","docs\u002Fbot-detection\u002F04.checkers\u002F01.ip-validation",{"title":444,"path":445,"stem":446},"Good \u002F Bad Bot Verification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgood-bots","docs\u002Fbot-detection\u002F04.checkers\u002F02.good-bots",{"title":448,"path":449,"stem":450},"Browser & Device Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbrowser-device","docs\u002Fbot-detection\u002F04.checkers\u002F03.browser-device",{"title":452,"path":453,"stem":454},"Locale Map","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Flocale-map","docs\u002Fbot-detection\u002F04.checkers\u002F04.locale-map",{"title":456,"path":457,"stem":458},"Known Threats","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-threats","docs\u002Fbot-detection\u002F04.checkers\u002F05.known-threats",{"title":460,"path":461,"stem":462},"ASN Classification","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fasn-classification","docs\u002Fbot-detection\u002F04.checkers\u002F06.asn-classification",{"title":464,"path":465,"stem":466},"Tor Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftor-analysis","docs\u002Fbot-detection\u002F04.checkers\u002F07.tor-analysis",{"title":468,"path":469,"stem":470},"Timezone Consistency","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Ftimezone-consistency","docs\u002Fbot-detection\u002F04.checkers\u002F08.timezone-consistency",{"title":472,"path":473,"stem":474},"Honeypot","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fhoneypot","docs\u002Fbot-detection\u002F04.checkers\u002F09.honeypot",{"title":476,"path":477,"stem":478},"Known Bad IPs","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ips","docs\u002Fbot-detection\u002F04.checkers\u002F10.known-bad-ips",{"title":480,"path":481,"stem":482},"Behavior Rate","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fbehavior-rate","docs\u002Fbot-detection\u002F04.checkers\u002F11.behavior-rate",{"title":484,"path":485,"stem":486},"Proxy \u002F ISP \u002F Cookie","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fproxy-isp-cookies","docs\u002Fbot-detection\u002F04.checkers\u002F12.proxy-isp-cookies",{"title":488,"path":489,"stem":490},"Session Coherence","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fsession-coherence","docs\u002Fbot-detection\u002F04.checkers\u002F13.session-coherence",{"title":492,"path":493,"stem":494},"Velocity Fingerprint","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fvelocity-fingerprint","docs\u002Fbot-detection\u002F04.checkers\u002F14.velocity-fingerprint",{"title":496,"path":497,"stem":498},"UA & Header Analysis","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fua-header","docs\u002Fbot-detection\u002F04.checkers\u002F15.ua-header",{"title":500,"path":501,"stem":502},"Geolocation","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fgeolocation","docs\u002Fbot-detection\u002F04.checkers\u002F16.geolocation",{"title":504,"path":505,"stem":506},"Known Bad User-Agents","\u002Fdocs\u002Fbot-detection\u002Fcheckers\u002Fknown-bad-ua","docs\u002Fbot-detection\u002F04.checkers\u002F17.known-bad-ua",{"title":38,"path":508,"stem":509},"\u002Fdocs\u002Fbot-detection\u002Fsecurity","docs\u002Fbot-detection\u002F04.security",{"title":246,"path":511,"stem":512},"\u002Fdocs\u002Fbot-detection\u002Fapi","docs\u002Fbot-detection\u002F05.api",{"title":237,"path":514,"stem":515},"\u002Fdocs\u002Fbot-detection\u002Fconfiguration","docs\u002Fbot-detection\u002F06.configuration",{"title":517,"path":11,"stem":518,"children":519},"Introduction","docs\u002Fgetting-started\u002Findex",[520],{"title":517,"path":11,"stem":518},{"title":27,"path":29,"stem":70,"children":522},[523,524,525,565,566,571,572],{"title":27,"path":29,"stem":70},{"title":14,"path":74,"stem":75},{"title":77,"path":78,"stem":79,"children":526},[527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549],{"title":77,"path":78,"stem":79},{"title":83,"path":84,"stem":85},{"title":87,"path":88,"stem":89},{"title":91,"path":92,"stem":93},{"title":95,"path":96,"stem":97},{"title":99,"path":100,"stem":101},{"title":103,"path":104,"stem":105},{"title":107,"path":108,"stem":109},{"title":111,"path":112,"stem":113},{"title":115,"path":116,"stem":117},{"title":119,"path":120,"stem":121},{"title":123,"path":124,"stem":125},{"title":127,"path":128,"stem":129},{"title":131,"path":132,"stem":133},{"title":135,"path":136,"stem":137},{"title":139,"path":140,"stem":141},{"title":143,"path":144,"stem":145},{"title":147,"path":148,"stem":149},{"title":151,"path":152,"stem":153},{"title":155,"path":156,"stem":157},{"title":159,"path":160,"stem":161},{"title":163,"path":164,"stem":165},{"title":167,"path":168,"stem":169,"children":550},[551,552,553,554,563,564],{"title":167,"path":168,"stem":169},{"title":173,"path":174,"stem":175},{"title":177,"path":178,"stem":179},{"title":181,"path":182,"stem":183,"children":555},[556,557,558,559,560,561,562],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189},{"title":191,"path":192,"stem":193},{"title":195,"path":196,"stem":197},{"title":199,"path":200,"stem":201},{"title":203,"path":204,"stem":205},{"title":207,"path":208,"stem":209},{"title":147,"path":211,"stem":212},{"title":38,"path":214,"stem":215},{"title":38,"path":217,"stem":218},{"title":220,"path":221,"stem":222,"children":567,"page":53},[568,569,570],{"title":225,"path":226,"stem":227},{"title":229,"path":230,"stem":231},{"title":233,"path":234,"stem":235},{"title":237,"path":238,"stem":239},{"title":241,"path":242,"stem":243,"children":573,"page":53},[574,575,576],{"title":246,"path":247,"stem":248},{"title":250,"path":251,"stem":252},{"title":254,"path":255,"stem":256},{"title":40,"path":42,"stem":578,"children":579},"docs\u002Fshield-base\u002Findex",[580,581,584,588,629,633,637,641],{"title":40,"path":42,"stem":578},{"title":14,"path":582,"stem":583},"\u002Fdocs\u002Fshield-base\u002Fgetting-started","docs\u002Fshield-base\u002F00.getting-started",{"title":585,"path":586,"stem":587},"CLI Reference","\u002Fdocs\u002Fshield-base\u002Fcli","docs\u002Fshield-base\u002F01.cli",{"title":411,"path":589,"stem":590,"children":591},"\u002Fdocs\u002Fshield-base\u002Fdata-sources","docs\u002Fshield-base\u002F02.data-sources\u002Findex",[592,593,597,601,605,609,613,617,621,625],{"title":411,"path":589,"stem":590},{"title":594,"path":595,"stem":596},"BGP \u002F ASN","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fbgp","docs\u002Fshield-base\u002F02.data-sources\u002Fbgp",{"title":598,"path":599,"stem":600},"City Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcity","docs\u002Fshield-base\u002F02.data-sources\u002Fcity",{"title":602,"path":603,"stem":604},"Country Geolocation","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcountry","docs\u002Fshield-base\u002F02.data-sources\u002Fcountry",{"title":606,"path":607,"stem":608},"Verified Crawlers","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fcrawlers","docs\u002Fshield-base\u002F02.data-sources\u002Fcrawlers",{"title":610,"path":611,"stem":612},"Disposable Emails","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Femail","docs\u002Fshield-base\u002F02.data-sources\u002Femail",{"title":614,"path":615,"stem":616},"FireHOL Threat Intelligence","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ffirehol","docs\u002Fshield-base\u002F02.data-sources\u002Ffirehol",{"title":618,"path":619,"stem":620},"Proxy Detection","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fproxy","docs\u002Fshield-base\u002F02.data-sources\u002Fproxy",{"title":622,"path":623,"stem":624},"Tor Nodes","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Ftor","docs\u002Fshield-base\u002F02.data-sources\u002Ftor",{"title":626,"path":627,"stem":628},"Suspicious User-Agents","\u002Fdocs\u002Fshield-base\u002Fdata-sources\u002Fuseragent","docs\u002Fshield-base\u002F02.data-sources\u002Fuseragent",{"title":630,"path":631,"stem":632},"Programmatic Usage","\u002Fdocs\u002Fshield-base\u002Fusage","docs\u002Fshield-base\u002F03.usage",{"title":634,"path":635,"stem":636},"Custom Data Sources","\u002Fdocs\u002Fshield-base\u002Fcustom-data-sources","docs\u002Fshield-base\u002F04.custom-data-sources",{"title":638,"path":639,"stem":640},"TypeScript Types","\u002Fdocs\u002Fshield-base\u002Ftypes","docs\u002Fshield-base\u002F05.types",{"title":246,"path":642,"stem":643},"\u002Fdocs\u002Fshield-base\u002Fapi","docs\u002Fshield-base\u002F06.api",{"title":395,"path":48,"stem":645,"children":646},"docs\u002Futils\u002Findex",[647,648,665,698,795],{"title":395,"path":48,"stem":645},{"title":649,"path":650,"stem":651,"children":652,"page":53},"Eslint","\u002Fdocs\u002Futils\u002Feslint","docs\u002Futils\u002Feslint",[653,657,661],{"title":654,"path":655,"stem":656},"React Config","\u002Fdocs\u002Futils\u002Feslint\u002Freact","docs\u002Futils\u002Feslint\u002Freact",{"title":658,"path":659,"stem":660},"TypeScript Config","\u002Fdocs\u002Futils\u002Feslint\u002Ftypescript","docs\u002Futils\u002Feslint\u002Ftypescript",{"title":662,"path":663,"stem":664},"Vue Config","\u002Fdocs\u002Futils\u002Feslint\u002Fvue","docs\u002Futils\u002Feslint\u002Fvue",{"title":666,"path":667,"stem":668,"children":669,"page":53},"Server","\u002Fdocs\u002Futils\u002Fserver","docs\u002Futils\u002Fserver",[670,674,678,682,686,690,694],{"title":671,"path":672,"stem":673},"Encryption","\u002Fdocs\u002Futils\u002Fserver\u002Fencryption","docs\u002Futils\u002Fserver\u002Fencryption",{"title":675,"path":676,"stem":677},"Path Resolver","\u002Fdocs\u002Futils\u002Fserver\u002Fpathresolver","docs\u002Futils\u002Fserver\u002FpathResolver",{"title":679,"path":680,"stem":681},"File Replacements","\u002Fdocs\u002Futils\u002Fserver\u002Freplace","docs\u002Futils\u002Fserver\u002Freplace",{"title":683,"path":684,"stem":685},"run","\u002Fdocs\u002Futils\u002Fserver\u002Frun","docs\u002Futils\u002Fserver\u002Frun",{"title":687,"path":688,"stem":689},"scheduleTask","\u002Fdocs\u002Futils\u002Fserver\u002Fscheduletask","docs\u002Futils\u002Fserver\u002FscheduleTask",{"title":691,"path":692,"stem":693},"spawnRun","\u002Fdocs\u002Futils\u002Fserver\u002Fspawnrun","docs\u002Futils\u002Fserver\u002FspawnRun",{"title":695,"path":696,"stem":697},"uploadCsv","\u002Fdocs\u002Futils\u002Fserver\u002Fuploadcsv","docs\u002Futils\u002Fserver\u002FuploadCsv",{"title":699,"path":700,"stem":701,"children":702,"page":53},"Shared","\u002Fdocs\u002Futils\u002Fshared","docs\u002Futils\u002Fshared",[703,707,711,715,719,723,727,731,735,739,743,747,751,755,759,763,767,771,775,779,783,787,791],{"title":704,"path":705,"stem":706},"BatchQueue","\u002Fdocs\u002Futils\u002Fshared\u002Fbatchqueue","docs\u002Futils\u002Fshared\u002FbatchQueue",{"title":708,"path":709,"stem":710},"capitalize","\u002Fdocs\u002Futils\u002Fshared\u002Fcapitalize","docs\u002Futils\u002Fshared\u002Fcapitalize",{"title":712,"path":713,"stem":714},"chunkProcess","\u002Fdocs\u002Futils\u002Fshared\u002Fchunkprocess","docs\u002Futils\u002Fshared\u002FchunkProcess",{"title":716,"path":717,"stem":718},"cleanObject","\u002Fdocs\u002Futils\u002Fshared\u002Fcleanobject","docs\u002Futils\u002Fshared\u002FcleanObject",{"title":720,"path":721,"stem":722},"createConfigManager","\u002Fdocs\u002Futils\u002Fshared\u002Fconfigurationdefiner","docs\u002Futils\u002Fshared\u002FconfigurationDefiner",{"title":724,"path":725,"stem":726},"debounce","\u002Fdocs\u002Futils\u002Fshared\u002Fdebounce","docs\u002Futils\u002Fshared\u002Fdebounce",{"title":728,"path":729,"stem":730},"ensureArray","\u002Fdocs\u002Futils\u002Fshared\u002Fensurearray","docs\u002Futils\u002Fshared\u002FensureArray",{"title":732,"path":733,"stem":734},"fetchWithRetry","\u002Fdocs\u002Futils\u002Fshared\u002Ffetchwithretry","docs\u002Futils\u002Fshared\u002FfetchWithRetry",{"title":736,"path":737,"stem":738},"filterEmptyValues","\u002Fdocs\u002Futils\u002Fshared\u002Ffilteremptyvalues","docs\u002Futils\u002Fshared\u002FfilterEmptyValues",{"title":740,"path":741,"stem":742},"findStringsInObject","\u002Fdocs\u002Futils\u002Fshared\u002Ffindobjectvalues","docs\u002Futils\u002Fshared\u002FfindObjectValues",{"title":744,"path":745,"stem":746},"fisherYatesShuffle","\u002Fdocs\u002Futils\u002Fshared\u002Ffisheryatesshuffle","docs\u002Futils\u002Fshared\u002FfisherYatesShuffle",{"title":748,"path":749,"stem":750},"getRandomImage","\u002Fdocs\u002Futils\u002Fshared\u002Fgetrandomimage","docs\u002Futils\u002Fshared\u002FgetRandomImage",{"title":752,"path":753,"stem":754},"isObjectHasValues","\u002Fdocs\u002Futils\u002Fshared\u002Fisobjecthasvalues","docs\u002Futils\u002Fshared\u002FisObjectHasValues",{"title":756,"path":757,"stem":758},"isAsyncOrPromise","\u002Fdocs\u002Futils\u002Fshared\u002Fispromise","docs\u002Futils\u002Fshared\u002FisPromise",{"title":760,"path":761,"stem":762},"MiniCache","\u002Fdocs\u002Futils\u002Fshared\u002Fminicache","docs\u002Futils\u002Fshared\u002FminiCache",{"title":764,"path":765,"stem":766},"parseCookies","\u002Fdocs\u002Futils\u002Fshared\u002Fparserawcookies","docs\u002Futils\u002Fshared\u002FparseRawCookies",{"title":768,"path":769,"stem":770},"safeAction","\u002Fdocs\u002Futils\u002Fshared\u002Fpromiselocker","docs\u002Futils\u002Fshared\u002FpromiseLocker",{"title":772,"path":773,"stem":774},"Random","\u002Fdocs\u002Futils\u002Fshared\u002Frandom","docs\u002Futils\u002Fshared\u002Frandom",{"title":776,"path":777,"stem":778},"range","\u002Fdocs\u002Futils\u002Fshared\u002Frange","docs\u002Futils\u002Fshared\u002Frange",{"title":780,"path":781,"stem":782},"rateLimiters","\u002Fdocs\u002Futils\u002Fshared\u002Fratelimiters","docs\u002Futils\u002Fshared\u002FrateLimiters",{"title":784,"path":785,"stem":786},"safeObjectMerge","\u002Fdocs\u002Futils\u002Fshared\u002Fsafemerge","docs\u002Futils\u002Fshared\u002FsafeMerge",{"title":788,"path":789,"stem":790},"textTruncation","\u002Fdocs\u002Futils\u002Fshared\u002Ftexttruncation","docs\u002Futils\u002Fshared\u002FtextTruncation",{"title":792,"path":793,"stem":794},"validateZodSchema","\u002Fdocs\u002Futils\u002Fshared\u002Fvalidatezodschema","docs\u002Futils\u002Fshared\u002FvalidateZodSchema",{"title":796,"path":797,"stem":798,"children":799},"Utility Types","\u002Fdocs\u002Futils\u002Ftypes","docs\u002Futils\u002Ftypes\u002Findex",[800,801,805,809,813,817,821,825,829,833],{"title":796,"path":797,"stem":798},{"title":802,"path":803,"stem":804},"Brand","\u002Fdocs\u002Futils\u002Ftypes\u002Fbrand","docs\u002Futils\u002Ftypes\u002FBrand",{"title":806,"path":807,"stem":808},"DeepPartial","\u002Fdocs\u002Futils\u002Ftypes\u002Fdeeppartial","docs\u002Futils\u002Ftypes\u002FDeepPartial",{"title":810,"path":811,"stem":812},"Merge","\u002Fdocs\u002Futils\u002Ftypes\u002Fmerge","docs\u002Futils\u002Ftypes\u002FMerge",{"title":814,"path":815,"stem":816},"NonNullable","\u002Fdocs\u002Futils\u002Ftypes\u002Fnonnullable","docs\u002Futils\u002Ftypes\u002FNonNullable",{"title":818,"path":819,"stem":820},"Prettify","\u002Fdocs\u002Futils\u002Ftypes\u002Fprettify","docs\u002Futils\u002Ftypes\u002FPrettify",{"title":822,"path":823,"stem":824},"PromiseType","\u002Fdocs\u002Futils\u002Ftypes\u002Fpromisetype","docs\u002Futils\u002Ftypes\u002FPromiseType",{"title":826,"path":827,"stem":828},"RequireKeys","\u002Fdocs\u002Futils\u002Ftypes\u002Frequirekeys","docs\u002Futils\u002Ftypes\u002FRequireKeys",{"title":830,"path":831,"stem":832},"StandardResponse","\u002Fdocs\u002Futils\u002Ftypes\u002Fstandardresponse","docs\u002Futils\u002Ftypes\u002FStandardResponse",{"title":834,"path":835,"stem":836},"ValueOf","\u002Fdocs\u002Futils\u002Ftypes\u002Fvalueof","docs\u002Futils\u002Ftypes\u002FValueOf",{"id":4,"extension":5,"links":838,"meta":849,"stem":62,"__hash__":63},[839,847,848],{"nested":8,"label":9,"icon":10,"to":11,"children":840},[841,842,843,844,845,846],{"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":851,"title":159,"body":852,"description":4437,"extension":4438,"icon":15,"meta":4439,"module":4440,"navigation":8,"path":160,"rawbody":4441,"seo":4442,"stem":161,"__hash__":4443},"docs\u002Fdocs\u002Fiam\u002F01.essentials\u002F19.service.md",{"type":853,"value":854,"toc":4402},"minimark",[855,876,883,886,891,894,956,981,987,989,994,1000,1005,1099,1172,1182,1184,1189,1200,1277,1281,1284,1561,1563,1567,1570,1828,1836,1838,1842,1845,1849,1852,1887,1903,1907,1910,1999,2030,2034,2037,2110,2119,2123,2126,2284,2298,2302,2305,2354,2367,2371,2374,2502,2527,2531,2534,2560,2573,2575,2579,2582,2634,2640,2642,2645,2648,2653,2655,2659,2662,2666,2669,2755,2759,2765,2826,2830,2833,2906,2925,2927,2931,2937,3004,3015,3021,3023,3027,3039,3045,3048,3493,3504,3508,3515,3836,3862,3866,3877,3917,3920,3938,3950,3952,3956,3959,4273,4275,4279,4398],[856,857,858,859,863,864,871,872,875],"p",{},"The IAM service ships two entry points. The ",[860,861,862],"code",{},"@riavzon\u002Fauth\u002Fservice"," export provides a complete standalone server that reads an ",[865,866,870],"a",{"href":867,"rel":868},"https:\u002F\u002Fgithub.com\u002FFiloSottile\u002Fage",[869],"nofollow","age","-encrypted configuration file, creates the Express application, and begins listening for requests. The ",[860,873,874],{},"@riavzon\u002Fauth"," export provides the same building blocks as individual functions and routers so you can mount them into your own Express application.",[856,877,878,879,882],{},"Both paths converge on the same core function: ",[860,880,881],{},"bootstrapApp",". This function takes a validated configuration object, creates every pool, table, middleware, and route the service needs, and returns a ready to use Express app.",[884,885],"hr",{},[887,888,890],"h2",{"id":889},"package-exports","Package Exports",[856,892,893],{},"The package exposes two subpath exports:",[895,896,897,913],"table",{},[898,899,900],"thead",{},[901,902,903,907,910],"tr",{},[904,905,906],"th",{},"Export",[904,908,909],{},"Entry",[904,911,912],{},"Purpose",[914,915,916,931],"tbody",{},[901,917,918,923,928],{},[919,920,921],"td",{},[860,922,874],{},[919,924,925],{},[860,926,927],{},"dist\u002Fmain.mjs",[919,929,930],{},"Library mode. Individual functions, middleware, routers, and utilities.",[901,932,933,937,942],{},[919,934,935],{},[860,936,862],{},[919,938,939],{},[860,940,941],{},"dist\u002Fservice.mjs",[919,943,944,945,947,948,951,952,955],{},"Standalone mode. Exports ",[860,946,881],{}," and ",[860,949,950],{},"startServer",". Auto-starts when ",[860,953,954],{},"NODE_ENV !== 'test'",".",[856,957,958,959,961,962,964,965,968,969,972,973,976,977,980],{},"When you import from ",[860,960,874],{},", nothing boots automatically. When you import from ",[860,963,862],{},", the module calls ",[860,966,967],{},"startServer()"," immediately unless ",[860,970,971],{},"NODE_ENV"," is ",[860,974,975],{},"test",". In production, this is the module you point your container's ",[860,978,979],{},"CMD"," at.",[856,982,983,984,955],{},"For a complete list of every export, see the ",[865,985,986],{"href":247},"API reference",[884,988],{},[887,990,992],{"id":991},"startserver",[860,993,950],{},[856,995,996,997,999],{},"The ",[860,998,950],{}," function is the outermost entry point in standalone mode. It handles configuration loading, application bootstrap, config file cleanup, and HTTP listening.",[1001,1002,1004],"h3",{"id":1003},"startup-flow","Startup Flow",[1006,1007,1009,1014,1029,1033,1047,1051,1058,1062,1077,1081],"steps",{"level":1008},"4",[1010,1011,1013],"h4",{"id":1012},"locate-the-configuration-file","Locate the configuration file",[856,1015,1016,1017,1020,1021,1024,1025,1028],{},"The function reads the ",[860,1018,1019],{},"CONFIG_PATH"," environment variable. If the variable is not set, it defaults to ",[860,1022,1023],{},"\u002Frun\u002Fapp\u002Fconfig.json",". It then checks that the file exists using ",[860,1026,1027],{},"fs.access",". If the file is missing, the process exits with a fatal error.",[1010,1030,1032],{"id":1031},"parse-configuration","Parse configuration",[856,1034,1035,1036,1039,1040,1042,1043,1046],{},"The file is read as UTF-8 and parsed with ",[860,1037,1038],{},"JSON.parse",". The resulting object is passed directly to ",[860,1041,881],{},", which validates it against the Zod schema internally. See the ",[865,1044,1045],{"href":238},"Configuration reference"," for the full schema.",[1010,1048,1050],{"id":1049},"bootstrap-the-application","Bootstrap the application",[856,1052,1053,1054,1057],{},"Calls ",[860,1055,1056],{},"bootstrapApp(config)",", which initializes all pools, tables, middleware, and routes. Returns a fully configured Express app.",[1010,1059,1061],{"id":1060},"delete-the-configuration-file","Delete the configuration file",[856,1063,1064,1065,1068,1069,1072,1073,1076],{},"After bootstrapping, the function deletes the configuration file from disk with ",[860,1066,1067],{},"fs.unlink",". This ensures that sensitive credentials (database passwords, JWT secrets, HMAC keys, API keys) do not persist on the filesystem after they have been loaded into memory. If the ",[860,1070,1071],{},"SKIP_CONFIG_UNLINK"," environment variable is set to ",[860,1074,1075],{},"'true'",", the file is kept.",[1010,1078,1080],{"id":1079},"start-listening","Start listening",[856,1082,1083,1084,1087,1088,1091,1092,1087,1095,1098],{},"The app begins listening on the configured ",[860,1085,1086],{},"service.port"," (default ",[860,1089,1090],{},"10000",") and ",[860,1093,1094],{},"service.ipAddress",[860,1096,1097],{},"0.0.0.0","). Once the server is ready, background data-refresh tasks are scheduled.",[1100,1101,1106],"pre",{"className":1102,"code":1103,"language":1104,"meta":1105,"style":1105},"language-ts shiki shiki-themes light-plus light-plus dracula","import { startServer } from '@riavzon\u002Fauth\u002Fservice'\n\n\u002F\u002F In production, this runs automatically.\n\u002F\u002F In tests (NODE_ENV=test), you call it manually:\nawait startServer()\n","ts","",[860,1107,1108,1140,1146,1153,1159],{"__ignoreMap":1105},[1109,1110,1113,1117,1121,1124,1127,1130,1134,1137],"span",{"class":1111,"line":1112},"line",1,[1109,1114,1116],{"class":1115},"sZ328","import",[1109,1118,1120],{"class":1119},"sDd4n"," { ",[1109,1122,950],{"class":1123},"sjsA6",[1109,1125,1126],{"class":1119}," } ",[1109,1128,1129],{"class":1115},"from",[1109,1131,1133],{"class":1132},"sFkSl"," '",[1109,1135,862],{"class":1136},"sFB1V",[1109,1138,1139],{"class":1132},"'\n",[1109,1141,1143],{"class":1111,"line":1142},2,[1109,1144,1145],{"emptyLinePlaceholder":8},"\n",[1109,1147,1149],{"class":1111,"line":1148},3,[1109,1150,1152],{"class":1151},"sghk6","\u002F\u002F In production, this runs automatically.\n",[1109,1154,1156],{"class":1111,"line":1155},4,[1109,1157,1158],{"class":1151},"\u002F\u002F In tests (NODE_ENV=test), you call it manually:\n",[1109,1160,1162,1165,1169],{"class":1111,"line":1161},5,[1109,1163,1164],{"class":1115},"await",[1109,1166,1168],{"class":1167},"sHOzp"," startServer",[1109,1170,1171],{"class":1119},"()\n",[1173,1174,1175],"warning",{},[856,1176,1177,1178,1181],{},"The config file is deleted after parsing. If the process crashes during bootstrap and the file has already been unlinked, you need to re-decrypt it before restarting. In a Docker deployment this happens automatically because ",[860,1179,1180],{},"decrypt.sh"," runs on every container start.",[884,1183],{},[887,1185,1187],{"id":1186},"bootstrapapp",[860,1188,881],{},[856,1190,1191,1192,1195,1196,1199],{},"The core bootstrap function. It accepts a ",[860,1193,1194],{},"ConfigurationInput"," object, performs all initialization, and returns an Express ",[860,1197,1198],{},"Application",". Both the standalone server and library consumers use this function.",[1100,1201,1203],{"className":1102,"code":1202,"language":1104,"meta":1105,"style":1105},"import { bootstrapApp } from '@riavzon\u002Fauth\u002Fservice'\nimport type { ConfigurationInput } from '@riavzon\u002Fauth'\n\nconst app = await bootstrapApp(config)\n",[860,1204,1205,1223,1244,1248],{"__ignoreMap":1105},[1109,1206,1207,1209,1211,1213,1215,1217,1219,1221],{"class":1111,"line":1112},[1109,1208,1116],{"class":1115},[1109,1210,1120],{"class":1119},[1109,1212,881],{"class":1123},[1109,1214,1126],{"class":1119},[1109,1216,1129],{"class":1115},[1109,1218,1133],{"class":1132},[1109,1220,862],{"class":1136},[1109,1222,1139],{"class":1132},[1109,1224,1225,1227,1230,1232,1234,1236,1238,1240,1242],{"class":1111,"line":1142},[1109,1226,1116],{"class":1115},[1109,1228,1229],{"class":1115}," type",[1109,1231,1120],{"class":1119},[1109,1233,1194],{"class":1123},[1109,1235,1126],{"class":1119},[1109,1237,1129],{"class":1115},[1109,1239,1133],{"class":1132},[1109,1241,874],{"class":1136},[1109,1243,1139],{"class":1132},[1109,1245,1246],{"class":1111,"line":1148},[1109,1247,1145],{"emptyLinePlaceholder":8},[1109,1249,1250,1254,1258,1262,1265,1268,1271,1274],{"class":1111,"line":1155},[1109,1251,1253],{"class":1252},"sl46w","const",[1109,1255,1257],{"class":1256},"s3JHE"," app",[1109,1259,1261],{"class":1260},"saOXh"," =",[1109,1263,1264],{"class":1115}," await",[1109,1266,1267],{"class":1167}," bootstrapApp",[1109,1269,1270],{"class":1119},"(",[1109,1272,1273],{"class":1123},"config",[1109,1275,1276],{"class":1119},")\n",[1001,1278,1280],{"id":1279},"bootstrap-sequence","Bootstrap Sequence",[856,1282,1283],{},"The function executes the following steps in order:",[1006,1285,1286,1290,1320,1324,1330,1378,1389,1393,1423,1427,1442,1446,1471,1475,1514,1518,1521,1525,1528,1532,1538,1542],{"level":1008},[1010,1287,1289],{"id":1288},"configure-oauth-providers","Configure OAuth providers",[856,1291,1292,1293,1296,1297,1300,1301,1304,1305,1308,1309,1312,1313,1316,1317,1319],{},"If the configuration includes a ",[860,1294,1295],{},"providers"," array, each entry is instantiated as an ",[860,1298,1299],{},"OAuthProvider"," object. Providers defined with a ",[860,1302,1303],{},"schema"," field use that Zod schema for profile validation. Providers defined with ",[860,1306,1307],{},"fields"," or ",[860,1310,1311],{},"useStandardProfile"," use the built-in ",[860,1314,1315],{},"StandardProfileSchema",". See ",[865,1318,111],{"href":112}," for provider configuration details.",[1010,1321,1323],{"id":1322},"initialize-the-configuration-manager","Initialize the configuration manager",[856,1325,1053,1326,1329],{},[860,1327,1328],{},"configuration(config)",", which passes the raw config through the Zod schema for validation, then runs three initialization tasks concurrently:",[1331,1332,1333,1349,1366],"ol",{},[1334,1335,1336,1340,1341,1344,1345,1348],"li",{},[1337,1338,1339],"strong",{},"Main MySQL pool"," - Creates a ",[860,1342,1343],{},"mysql2\u002Fpromise"," connection pool from ",[860,1346,1347],{},"config.store.main",". This pool handles all authentication queries (users, refresh tokens, MFA codes).",[1334,1350,1351,1354,1355,1344,1358,1361,1362,1365],{},[1337,1352,1353],{},"Rate limiter pool"," - Creates a callback-based ",[860,1356,1357],{},"mysql2",[860,1359,1360],{},"config.store.rate_limiters_pool.store",". This separate pool is used exclusively by ",[865,1363,1364],{"href":148},"rate-limiter-flexible"," for counter storage.",[1334,1367,1368,1371,1372,1377],{},[1337,1369,1370],{},"Disposable email database"," - Opens an ",[865,1373,1376],{"href":1374,"rel":1375},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLightning_Memory-Mapped_Database",[869],"LMDB"," read-only database containing known disposable email domains. Used by the signup flow to reject temporary email addresses.",[856,1379,1380,1381,1384,1385,1388],{},"Once all three tasks complete, the configuration is frozen with ",[860,1382,1383],{},"Object.freeze"," and stored as a module-level singleton. Any subsequent call to ",[860,1386,1387],{},"getConfiguration()"," returns this frozen object.",[1010,1390,1392],{"id":1391},"create-the-express-application","Create the Express application",[856,1394,1395,1396,1401,1402,972,1405,1408,1409,1308,1412,1414,1415,1418,1419,1422],{},"Creates a new Express app and conditionally configures ",[865,1397,1400],{"href":1398,"rel":1399},"https:\u002F\u002Fexpressjs.com\u002Fen\u002Fguide\u002Fbehind-proxies.html",[869],"trust proxy",". When ",[860,1403,1404],{},"service.proxy.trust",[860,1406,1407],{},"true",", the app trusts the configured proxy IP (",[860,1410,1411],{},"service.proxy.server",[860,1413,1094],{},") and the client-facing proxy IP (",[860,1416,1417],{},"service.proxy.ipToTrust","). This ensures ",[860,1420,1421],{},"req.ip"," reflects the real client IP rather than the proxy's address.",[1010,1424,1426],{"id":1425},"register-the-health-endpoint","Register the health endpoint",[856,1428,1429,1430,1433,1434,1437,1438,1441],{},"Mounts ",[860,1431,1432],{},"GET \u002Fhealth"," before any middleware. The handler returns ",[860,1435,1436],{},"200 OK"," as plain text. Because it is registered before HMAC authentication, it is accessible without credentials. The Docker health check (",[860,1439,1440],{},"healthcheck.js",") polls this endpoint every 30 seconds.",[1010,1443,1445],{"id":1444},"configure-the-bot-detector","Configure the bot detector",[856,1447,1448,1449,1454,1455,1458,1459,1462,1463,1466,1467,1470],{},"Initializes the ",[865,1450,1451],{"href":35},[860,1452,1453],{},"@riavzon\u002Fbot-detector"," module. If the configuration has ",[860,1456,1457],{},"botDetector.enableBotDetector: true"," with custom ",[860,1460,1461],{},"settings",", those settings are used. Otherwise, a default configuration is applied. If bot detection is disabled (",[860,1464,1465],{},"enableBotDetector: false","), a passive-mode configuration is loaded that disables all active checks but still provides the ",[860,1468,1469],{},"ApiResponse"," middleware for visitor tracking.",[1010,1472,1474],{"id":1473},"create-database-tables","Create database tables",[856,1476,1477,1478,1481,1482,1485,1486,1489,1490,1493,1494,1497,1498,1501,1502,1505,1506,1489,1508,1511,1512,1046],{},"Creates the bot-detector tables (via ",[860,1479,1480],{},"createTables",") and the IAM tables (via\n",[860,1483,1484],{},"makeTables","). The IAM schema includes four tables: ",[860,1487,1488],{},"users",", ",[860,1491,1492],{},"api_tokens",",\n",[860,1495,1496],{},"refresh_tokens",", and ",[860,1499,1500],{},"mfa_codes",". All table creation statements use\n",[860,1503,1504],{},"CREATE TABLE IF NOT EXISTS",", so repeated calls are safe. After creating\n",[860,1507,1492],{},[860,1509,1510],{},"makeTables()"," also ensures the three API-token indexes exist and\nignores duplicate-key errors on repeated runs. See\n",[865,1513,151],{"href":152},[1010,1515,1517],{"id":1516},"mount-middleware-stack","Mount middleware stack",[856,1519,1520],{},"The middleware is registered in a specific order. Each layer depends on the layers before it.",[1010,1522,1524],{"id":1523},"mount-route-groups","Mount route groups",[856,1526,1527],{},"The public API verification router is registered first. After that, the\nauthenticated route groups are mounted in order: authentication routes, token\nrotation routes, magic link routes, BFF access routes, and API token\nmanagement routes. The standalone operational config endpoint is mounted after\nthose routers.",[1010,1529,1531],{"id":1530},"warm-up-the-bot-detector","Warm up the bot detector",[856,1533,1053,1534,1537],{},[860,1535,1536],{},"warmUp()"," from the bot-detector module to preload geo, ASN, and threat databases into memory. This runs after route registration so that the first incoming request does not pay the cold-start penalty.",[1010,1539,1541],{"id":1540},"register-terminal-handlers","Register terminal handlers",[856,1543,1544,1545,1548,1549,1552,1553,1556,1557,1560],{},"Mounts the ",[860,1546,1547],{},"notFoundHandler"," (returns ",[860,1550,1551],{},"404"," for unmatched paths) and ",[860,1554,1555],{},"finalUnHandledErrors"," (catches unhandled errors and returns ",[860,1558,1559],{},"500","). These are registered last so they only trigger when no route or middleware handled the request.",[884,1562],{},[887,1564,1566],{"id":1565},"middleware-stack","Middleware Stack",[856,1568,1569],{},"The middleware is mounted in a fixed order. Every request passes through each layer sequentially:",[895,1571,1572,1587],{},[898,1573,1574],{},[901,1575,1576,1579,1582,1584],{},[904,1577,1578],{},"Order",[904,1580,1581],{},"Middleware",[904,1583,912],{},[904,1585,1586],{},"Reference",[914,1588,1589,1620,1640,1667,1691,1717,1753,1773,1788,1812],{},[901,1590,1591,1594,1599,1616],{},[919,1592,1593],{},"1",[919,1595,1596],{},[860,1597,1598],{},"httpLogger",[919,1600,1601,1602,1607,1608,1611,1612,1615],{},"Structured request logging via ",[865,1603,1606],{"href":1604,"rel":1605},"https:\u002F\u002Fgithub.com\u002Fpinojs\u002Fpino-http",[869],"pino-http",". Assigns a unique ",[860,1609,1610],{},"X-Request-Id"," header to every request. Redacts ",[860,1613,1614],{},"Authorization"," headers and session cookies from logs.",[919,1617,1618],{},[865,1619,143],{"href":144},[901,1621,1622,1625,1631,1638],{},[919,1623,1624],{},"2",[919,1626,1627,1630],{},[860,1628,1629],{},"x-powered-by"," disabled",[919,1632,1633,1634,1637],{},"Removes the default Express ",[860,1635,1636],{},"X-Powered-By"," header to avoid fingerprinting the server technology.",[919,1639],{},[901,1641,1642,1645,1650,1665],{},[919,1643,1644],{},"3",[919,1646,1647],{},[860,1648,1649],{},"helmet",[919,1651,1652,1653,1489,1656,1489,1659,1489,1662,955],{},"Sets security headers: ",[860,1654,1655],{},"X-Frame-Options: DENY",[860,1657,1658],{},"Content-Security-Policy: frame-ancestors 'none'",[860,1660,1661],{},"Cross-Origin-Embedder-Policy",[860,1663,1664],{},"Referrer-Policy: origin",[919,1666],{},[901,1668,1669,1671,1676,1689],{},[919,1670,1008],{},[919,1672,1673],{},[860,1674,1675],{},"headers",[919,1677,1678,1679,1489,1682,1489,1685,1688],{},"Sets ",[860,1680,1681],{},"Cache-Control: no-cache, private, max-age=0",[860,1683,1684],{},"Pragma: no-cache",[860,1686,1687],{},"Expires: 0"," on every response. Prevents browsers and proxies from caching sensitive authentication responses.",[919,1690],{},[901,1692,1693,1696,1701,1715],{},[919,1694,1695],{},"5",[919,1697,1698],{},[860,1699,1700],{},"validateIp",[919,1702,1703,1704,1706,1707,1710,1711,1714],{},"Validates that ",[860,1705,1421],{}," is a valid IP address using Node.js ",[860,1708,1709],{},"net.isIP()",". Returns ",[860,1712,1713],{},"403 Forbidden"," if the IP is missing or invalid.",[919,1716],{},[901,1718,1719,1722,1728,1748],{},[919,1720,1721],{},"6",[919,1723,1724,1727],{},[860,1725,1726],{},"hmacAuth"," (conditional)",[919,1729,1730,1731,1734,1735,1489,1738,1489,1741,1497,1744,1747],{},"Only mounted when ",[860,1732,1733],{},"service.Hmac"," is configured. Verifies the ",[860,1736,1737],{},"X-Client-Id",[860,1739,1740],{},"X-Timestamp",[860,1742,1743],{},"X-Signature",[860,1745,1746],{},"X-Request-ID"," headers against a shared secret. Rejects requests with missing headers, unknown client IDs, expired timestamps, replayed nonces, or invalid signatures. Bypasses the health endpoint on localhost.",[919,1749,1750],{},[865,1751,1752],{"href":136},"HMAC",[901,1754,1755,1758,1763,1769],{},[919,1756,1757],{},"7",[919,1759,1760],{},[860,1761,1762],{},"apiVerificationRoute()",[919,1764,1429,1765,1768],{},[860,1766,1767],{},"GET \u002Fapi\u002Fpublic\u002Fverify"," before body and cookie parsing.",[919,1770,1771],{},[865,1772,167],{"href":178},[901,1774,1775,1778,1783,1786],{},[919,1776,1777],{},"8",[919,1779,1780],{},[860,1781,1782],{},"express.json()",[919,1784,1785],{},"Parses JSON request bodies.",[919,1787],{},[901,1789,1790,1793,1798,1808],{},[919,1791,1792],{},"9",[919,1794,1795],{},[860,1796,1797],{},"cookieParser()",[919,1799,1800,1801,1804,1805,955],{},"Parses ",[860,1802,1803],{},"Cookie"," headers into ",[860,1806,1807],{},"req.cookies",[919,1809,1810],{},[865,1811,155],{"href":156},[901,1813,1814,1817,1821,1824],{},[919,1815,1816],{},"10",[919,1818,1819],{},[860,1820,1469],{},[919,1822,1823],{},"Bot-detector middleware that attaches visitor data and geo information to every request.",[919,1825,1826],{},[865,1827,33],{"href":35},[1173,1829,1830],{},[856,1831,1832,1833,1835],{},"The middleware and route order is security-critical. IP validation runs before\nHMAC so that requests with spoofed or missing IPs are rejected before\nsignature verification begins. The public API verification route is mounted\nbefore body parsing and cookie parsing because it does not need either. The\n",[860,1834,1469],{}," middleware is last because it relies on cookies being parsed.",[884,1837],{},[887,1839,1841],{"id":1840},"route-groups","Route Groups",[856,1843,1844],{},"After the early health check and service-wide middleware, six route groups are\nmounted. Five are authenticated or semi-public routers, and the operational\nconfig endpoint is mounted afterward as a direct handler.",[1001,1846,1848],{"id":1847},"public-api-verification-route","Public API Verification Route",[856,1850,1851],{},"Handles machine-to-machine API token verification.",[895,1853,1854,1868],{},[898,1855,1856],{},[901,1857,1858,1861,1864,1866],{},[904,1859,1860],{},"Method",[904,1862,1863],{},"Path",[904,1865,912],{},[904,1867,1586],{},[914,1869,1870],{},[901,1871,1872,1875,1880,1883],{},[919,1873,1874],{},"GET",[919,1876,1877],{},[860,1878,1879],{},"\u002Fapi\u002Fpublic\u002Fverify",[919,1881,1882],{},"Verify an API token against a required privilege",[919,1884,1885],{},[865,1886,167],{"href":178},[856,1888,1889,1890,1489,1892,1894,1895,1898,1899,1902],{},"This route is mounted before ",[860,1891,1782],{},[860,1893,1797],{},", and the\nbot-detector middleware. It reads the token from the ",[860,1896,1897],{},"x-api-key"," header and the\nrequired privilege from the ",[860,1900,1901],{},"privilege"," query parameter.",[1001,1904,1906],{"id":1905},"authentication-routes","Authentication Routes",[856,1908,1909],{},"Handles account creation, login, and OAuth flows.",[895,1911,1912,1926],{},[898,1913,1914],{},[901,1915,1916,1918,1920,1922,1924],{},[904,1917,1860],{},[904,1919,1863],{},[904,1921,912],{},[904,1923,1581],{},[904,1925,1586],{},[914,1927,1928,1954,1976],{},[901,1929,1930,1933,1938,1941,1950],{},[919,1931,1932],{},"POST",[919,1934,1935],{},[860,1936,1937],{},"\u002Fsignup",[919,1939,1940],{},"Create a new account",[919,1942,1943,1489,1946,1949],{},[860,1944,1945],{},"contentType('application\u002Fjson')",[860,1947,1948],{},"json({ limit: '1kb' })",", canary cookie check",[919,1951,1952],{},[865,1953,99],{"href":100},[901,1955,1956,1958,1963,1966,1972],{},[919,1957,1932],{},[919,1959,1960],{},[860,1961,1962],{},"\u002Flogin",[919,1964,1965],{},"Authenticate with email and password",[919,1967,1968,1489,1970,1949],{},[860,1969,1945],{},[860,1971,1948],{},[919,1973,1974],{},[865,1975,103],{"href":104},[901,1977,1978,1980,1985,1988,1995],{},[919,1979,1932],{},[919,1981,1982],{},[860,1983,1984],{},"\u002Fauth\u002FOAuth\u002F:providerName",[919,1986,1987],{},"Authenticate via an OAuth provider",[919,1989,1990,1489,1992,1949],{},[860,1991,1945],{},[860,1993,1994],{},"json({ limit: '4kb' })",[919,1996,1997],{},[865,1998,111],{"href":112},[856,2000,2001,2002,2005,2006,2009,2010,2013,2014,2017,2018,2021,2022,2025,2026,2029],{},"All three routes enforce a ",[860,2003,2004],{},"Content-Type: application\u002Fjson"," header via the ",[860,2007,2008],{},"contentType"," middleware. The ",[860,2011,2012],{},"json()"," middleware limits request bodies to 1 KB (4 KB for OAuth) and rejects empty bodies with a ",[860,2015,2016],{},"403",". Each route also requires the ",[860,2019,2020],{},"canary_id"," cookie from the ",[865,2023,2024],{"href":35},"bot-detector"," and returns ",[860,2027,2028],{},"400"," if it is missing.",[1001,2031,2033],{"id":2032},"token-rotation-routes","Token Rotation Routes",[856,2035,2036],{},"Handles token refresh and logout.",[895,2038,2039,2053],{},[898,2040,2041],{},[901,2042,2043,2045,2047,2049,2051],{},[904,2044,1860],{},[904,2046,1863],{},[904,2048,912],{},[904,2050,1581],{},[904,2052,1586],{},[914,2054,2055,2085],{},[901,2056,2057,2059,2064,2067,2081],{},[919,2058,1932],{},[919,2060,2061],{},[860,2062,2063],{},"\u002Fauth\u002Fuser\u002Frefresh-session",[919,2065,2066],{},"Rotate both access and refresh tokens",[919,2068,2069,1489,2072,1489,2075,1489,2078],{},[860,2070,2071],{},"requireRefreshToken",[860,2073,2074],{},"acceptCookieOnly",[860,2076,2077],{},"getFingerPrint",[860,2079,2080],{},"checkForActiveMfa",[919,2082,2083],{},[865,2084,91],{"href":92},[901,2086,2087,2089,2094,2097,2106],{},[919,2088,1932],{},[919,2090,2091],{},[860,2092,2093],{},"\u002Fauth\u002Flogout",[919,2095,2096],{},"Revoke the current session",[919,2098,2099,1489,2101,1489,2104],{},[860,2100,2071],{},[860,2102,2103],{},"requireAccessToken",[860,2105,2074],{},[919,2107,2108],{},[865,2109,107],{"href":108},[856,2111,996,2112,2114,2115,2118],{},[860,2113,2074],{}," middleware enforces that these requests send only cookies: no JSON body, no query string, and no ",[860,2116,2117],{},"Content-Type"," header. This prevents CSRF attacks by ensuring the request cannot carry attacker-controlled data beyond the browser's automatic cookie attachment.",[1001,2120,2122],{"id":2121},"magic-link-routes","Magic Link Routes",[856,2124,2125],{},"Handles MFA verification, password reset, custom MFA flows, and email changes.",[895,2127,2128,2140],{},[898,2129,2130],{},[901,2131,2132,2134,2136,2138],{},[904,2133,1860],{},[904,2135,1863],{},[904,2137,912],{},[904,2139,1586],{},[914,2141,2142,2158,2173,2189,2205,2220,2237,2253,2269],{},[901,2143,2144,2146,2151,2154],{},[919,2145,1874],{},[919,2147,2148],{},[860,2149,2150],{},"\u002Fauth\u002Fverify-mfa",[919,2152,2153],{},"Verify an email MFA link (redirects)",[919,2155,2156],{},[865,2157,123],{"href":124},[901,2159,2160,2162,2166,2169],{},[919,2161,1932],{},[919,2163,2164],{},[860,2165,2150],{},[919,2167,2168],{},"Submit an MFA code",[919,2170,2171],{},[865,2172,123],{"href":124},[901,2174,2175,2177,2182,2185],{},[919,2176,1932],{},[919,2178,2179],{},[860,2180,2181],{},"\u002Fcustom\u002Fmfa\u002F:reason",[919,2183,2184],{},"Initiate a custom MFA flow",[919,2186,2187],{},[865,2188,123],{"href":124},[901,2190,2191,2193,2198,2201],{},[919,2192,1874],{},[919,2194,2195],{},[860,2196,2197],{},"\u002Fauth\u002Fverify-custom-mfa",[919,2199,2200],{},"Verify a custom MFA link",[919,2202,2203],{},[865,2204,123],{"href":124},[901,2206,2207,2209,2213,2216],{},[919,2208,1932],{},[919,2210,2211],{},[860,2212,2197],{},[919,2214,2215],{},"Submit a custom MFA code",[919,2217,2218],{},[865,2219,123],{"href":124},[901,2221,2222,2224,2229,2232],{},[919,2223,1932],{},[919,2225,2226],{},[860,2227,2228],{},"\u002Fupdate\u002Femail",[919,2230,2231],{},"Change the user's email address",[919,2233,2234],{},[865,2235,123],{"href":2236},"\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#email-update-flow",[901,2238,2239,2241,2246,2249],{},[919,2240,1932],{},[919,2242,2243],{},[860,2244,2245],{},"\u002Fauth\u002Fforgot-password",[919,2247,2248],{},"Initiate a password reset",[919,2250,2251],{},[865,2252,163],{"href":164},[901,2254,2255,2257,2262,2265],{},[919,2256,1874],{},[919,2258,2259],{},[860,2260,2261],{},"\u002Fauth\u002Freset-password",[919,2263,2264],{},"Verify a password reset link",[919,2266,2267],{},[865,2268,163],{"href":164},[901,2270,2271,2273,2277,2280],{},[919,2272,1932],{},[919,2274,2275],{},[860,2276,2261],{},[919,2278,2279],{},"Submit a new password",[919,2281,2282],{},[865,2283,163],{"href":164},[856,2285,2286,2287,2289,2290,2293,2294,2297],{},"Custom MFA and email update routes require a valid access token, refresh\ntoken, fingerprint, and a ",[860,2288,2080],{}," pass before ",[860,2291,2292],{},"protectRoute"," runs\nfull ",[865,2295,2296],{"href":96},"anomaly detection"," on these requests.",[1001,2299,2301],{"id":2300},"bff-access-routes","BFF Access Routes",[856,2303,2304],{},"Backend-for-Frontend routes used by your application server to verify user sessions.",[895,2306,2307,2319],{},[898,2308,2309],{},[901,2310,2311,2313,2315,2317],{},[904,2312,1860],{},[904,2314,1863],{},[904,2316,912],{},[904,2318,1586],{},[914,2320,2321,2338],{},[901,2322,2323,2325,2330,2333],{},[919,2324,1874],{},[919,2326,2327],{},[860,2328,2329],{},"\u002Fsecret\u002Fdata",[919,2331,2332],{},"Verify the user's session and return authorization data",[919,2334,2335],{},[865,2336,2337],{"href":132},"BFF",[901,2339,2340,2342,2347,2350],{},[919,2341,1874],{},[919,2343,2344],{},[860,2345,2346],{},"\u002Fsecret\u002Faccesstoken\u002Fmetadata",[919,2348,2349],{},"Return decoded access token payload and TTL",[919,2351,2352],{},[865,2353,2337],{"href":132},[856,2355,2356,2357,2360,2361,2363,2364,2366],{},"Both routes require an access token (",[860,2358,2359],{},"Authorization: Bearer"," header), a refresh\ntoken (session cookie), and a valid fingerprint. The routes also call\n",[860,2362,2080],{}," before ",[860,2365,2292],{}," performs full JWT verification and\nanomaly detection.",[1001,2368,2370],{"id":2369},"api-token-management-routes","API Token Management Routes",[856,2372,2373],{},"Handles authenticated creation and management of API tokens.",[895,2375,2376,2388],{},[898,2377,2378],{},[901,2379,2380,2382,2384,2386],{},[904,2381,1860],{},[904,2383,1863],{},[904,2385,912],{},[904,2387,1586],{},[914,2389,2390,2406,2422,2438,2454,2470,2486],{},[901,2391,2392,2394,2399,2402],{},[919,2393,1932],{},[919,2395,2396],{},[860,2397,2398],{},"\u002Fapi\u002Fmanage\u002Fnew-token",[919,2400,2401],{},"Create a new API token",[919,2403,2404],{},[865,2405,173],{"href":174},[901,2407,2408,2410,2415,2418],{},[919,2409,1874],{},[919,2411,2412],{},[860,2413,2414],{},"\u002Fapi\u002Fmanage\u002Flist-metadata",[919,2416,2417],{},"List the user's valid API tokens",[919,2419,2420],{},[865,2421,207],{"href":208},[901,2423,2424,2426,2431,2434],{},[919,2425,1932],{},[919,2427,2428],{},[860,2429,2430],{},"\u002Fapi\u002Fmanage\u002Frevoke",[919,2432,2433],{},"Revoke an API token",[919,2435,2436],{},[865,2437,191],{"href":192},[901,2439,2440,2442,2447,2450],{},[919,2441,1932],{},[919,2443,2444],{},[860,2445,2446],{},"\u002Fapi\u002Fmanage\u002Fmetadata",[919,2448,2449],{},"Return metadata for one API token",[919,2451,2452],{},[865,2453,203],{"href":204},[901,2455,2456,2458,2463,2466],{},[919,2457,1932],{},[919,2459,2460],{},[860,2461,2462],{},"\u002Fapi\u002Fmanage\u002Frotate",[919,2464,2465],{},"Rotate an API token",[919,2467,2468],{},[865,2469,195],{"href":196},[901,2471,2472,2474,2479,2482],{},[919,2473,1932],{},[919,2475,2476],{},[860,2477,2478],{},"\u002Fapi\u002Fmanage\u002Fip-restriction-update",[919,2480,2481],{},"Update a token IP whitelist",[919,2483,2484],{},[865,2485,199],{"href":200},[901,2487,2488,2490,2495,2498],{},[919,2489,1932],{},[919,2491,2492],{},[860,2493,2494],{},"\u002Fapi\u002Fmanage\u002Fprivilege-update",[919,2496,2497],{},"Update a token privilege",[919,2499,2500],{},[865,2501,187],{"href":188},[856,2503,2504,2505,1493,2507,1489,2509,1489,2511,2513,2514,2516,2517,947,2519,2522,2523,2526],{},"All API token management routes require ",[860,2506,2103],{},[860,2508,2071],{},[860,2510,2077],{},[860,2512,2080],{},", and\n",[860,2515,2292],{},". The POST routes also enforce\n",[860,2518,1945],{},[860,2520,2521],{},"express.json({ limit: '1kb' })",". The\n",[860,2524,2525],{},"GET \u002Fapi\u002Fmanage\u002Flist-metadata"," branch skips the JSON guards because it does\nnot accept a request body.",[1001,2528,2530],{"id":2529},"operational-config-endpoint","Operational Config Endpoint",[856,2532,2533],{},"A single endpoint mounted outside the route groups:",[895,2535,2536,2546],{},[898,2537,2538],{},[901,2539,2540,2542,2544],{},[904,2541,1860],{},[904,2543,1863],{},[904,2545,912],{},[914,2547,2548],{},[901,2549,2550,2552,2557],{},[919,2551,1874],{},[919,2553,2554],{},[860,2555,2556],{},"\u002Foperational\u002Fconfig",[919,2558,2559],{},"Return the cookie domain and access token TTL to a trusted client",[856,2561,2562,2563,2566,2567,1308,2570,2572],{},"This endpoint is restricted by physical IP address. It compares ",[860,2564,2565],{},"req.socket.remoteAddress"," against ",[860,2568,2569],{},"service.clientIp",[860,2571,1417],{},". Only the configured trusted client can access it.",[884,2574],{},[887,2576,2578],{"id":2577},"terminal-handlers","Terminal Handlers",[856,2580,2581],{},"Two handlers are registered after all routes:",[895,2583,2584,2597],{},[898,2585,2586],{},[901,2587,2588,2591,2594],{},[904,2589,2590],{},"Handler",[904,2592,2593],{},"Status",[904,2595,2596],{},"Body",[914,2598,2599,2614],{},[901,2600,2601,2605,2609],{},[919,2602,2603],{},[860,2604,1547],{},[919,2606,2607],{},[860,2608,1551],{},[919,2610,2611],{},[860,2612,2613],{},"{ \"error\": \"The page you are looking for doesn't exists\" }",[901,2615,2616,2620,2629],{},[919,2617,2618],{},[860,2619,1555],{},[919,2621,2622,2624,2625,2628],{},[860,2623,1559],{}," (or ",[860,2626,2627],{},"res.statusCode"," if > 415)",[919,2630,2631],{},[860,2632,2633],{},"{ \"error\": \"\u003Cmessage>\" }",[856,2635,2636,2637,2639],{},"The error handler logs the full error via pino before sending the response. It picks the status code from the current ",[860,2638,2627],{}," if it is above 415, otherwise defaults to 500.",[884,2641],{},[887,2643,237],{"id":2644},"configuration",[856,2646,2647],{},"The configuration object is validated at startup by a strict Zod schema. Every field is type-checked at runtime. If validation fails, the process exits with a detailed error showing which fields failed and why.",[856,2649,2650,2651,955],{},"The schema covers database connections, JWT settings, password hashing, bot detection, rate limiting, magic links, OAuth providers, email credentials, and standalone server options. Each section is documented in its own essentials page, and the complete schema reference is available in the ",[865,2652,1045],{"href":238},[884,2654],{},[887,2656,2658],{"id":2657},"docker-deployment","Docker Deployment",[856,2660,2661],{},"The standalone service is designed to run as a Docker container with encrypted configuration.",[1001,2663,2665],{"id":2664},"container-architecture","Container Architecture",[856,2667,2668],{},"The Dockerfile uses a multi-stage build:",[1006,2670,2671,2675,2688,2692,2713,2717,2732,2736],{"level":1008},[1010,2672,2674],{"id":2673},"go-builder-stage","Go builder stage",[856,2676,2677,2678,2681,2682,2687],{},"Compiles the ",[865,2679,870],{"href":867,"rel":2680},[869]," encryption tool and ",[865,2683,2686],{"href":2684,"rel":2685},"https:\u002F\u002Fgithub.com\u002Fipinfo\u002Fmmdbctl",[869],"mmdbctl"," from source. These binaries are copied into later stages.",[1010,2689,2691],{"id":2690},"base-stage","Base stage",[856,2693,2694,2695,2698,2699,1489,2702,1489,2705,2708,2709,2712],{},"Starts from ",[860,2696,2697],{},"node:lts-slim",". Installs minimal runtime dependencies (",[860,2700,2701],{},"libatomic1",[860,2703,2704],{},"curl",[860,2706,2707],{},"ca-certificates","). Creates a non-root user (",[860,2710,2711],{},"appuser",", UID 10001) for the production stage.",[1010,2714,2716],{"id":2715},"builder-stage","Builder stage",[856,2718,2719,2720,2723,2724,2727,2728,2731],{},"Installs npm dependencies with ",[860,2721,2722],{},"npm ci",", runs ",[860,2725,2726],{},"bot-detector init"," to generate the ",[865,2729,2730],{"href":42},"shield-base"," data sources, and builds the TypeScript source with the configured build tool.",[1010,2733,2735],{"id":2734},"production-stage","Production stage",[856,2737,2738,2739,2742,2743,947,2745,2747,2748,2750,2751,2754],{},"Copies only the built artifacts, production ",[860,2740,2741],{},"node_modules",", bot-detector data sources, and the ",[860,2744,1180],{},[860,2746,1440],{}," scripts. Drops all development tools, npm itself, and build caches. Runs as ",[860,2749,2711],{}," (non-root) with ",[860,2752,2753],{},"no-new-privileges"," enforced.",[1001,2756,2758],{"id":2757},"entrypoint-flow","Entrypoint Flow",[856,2760,2761,2762,2764],{},"The container entrypoint is ",[860,2763,1180],{},", which runs before the main process:",[1006,2766,2767,2771,2777,2781,2803,2807],{"level":1008},[1010,2768,2770],{"id":2769},"check-for-existing-config","Check for existing config",[856,2772,2773,2774,2776],{},"If ",[860,2775,1023],{}," already exists (from a previous run or manual mount), the script skips decryption and proceeds directly to the main command.",[1010,2778,2780],{"id":2779},"decrypt-the-configuration","Decrypt the configuration",[856,2782,2783,2784,2787,2788,2791,2792,2795,2796,2798,2799,2802],{},"Reads the age private key from ",[860,2785,2786],{},"\u002Frun\u002Fsecrets\u002Fage_key"," and the encrypted config from ",[860,2789,2790],{},"\u002Frun\u002Fsecrets\u002Fencrypted_config",". Decrypts using ",[860,2793,2794],{},"age -d"," and writes the result to ",[860,2797,1023],{}," with ",[860,2800,2801],{},"0400"," permissions (read-only for the owner).",[1010,2804,2806],{"id":2805},"launch-the-service","Launch the service",[856,2808,1053,2809,2812,2813,2815,2816,2819,2820,2822,2823,2825],{},[860,2810,2811],{},"exec \"$@\"",", which replaces the shell process with the Docker ",[860,2814,979],{},": ",[860,2817,2818],{},"node dist\u002Fservice.mjs",". The service ",[860,2821,950],{}," function then reads the config from the default path (",[860,2824,1023],{},").",[1001,2827,2829],{"id":2828},"health-check","Health Check",[856,2831,2832],{},"Docker runs a health check every 30 seconds:",[1100,2834,2838],{"className":2835,"code":2836,"filename":1440,"language":2837,"meta":1105,"style":1105},"language-js shiki shiki-themes light-plus light-plus dracula","http.request({ host: '127.0.0.1', port: 10000, path: '\u002Fhealth', timeout: 2000 })\n","js",[860,2839,2840],{"__ignoreMap":1105},[1109,2841,2842,2845,2847,2850,2853,2856,2860,2862,2865,2868,2870,2873,2875,2879,2881,2884,2886,2888,2891,2893,2895,2898,2900,2903],{"class":1111,"line":1112},[1109,2843,2844],{"class":1123},"http",[1109,2846,955],{"class":1119},[1109,2848,2849],{"class":1167},"request",[1109,2851,2852],{"class":1119},"({ ",[1109,2854,2855],{"class":1123},"host",[1109,2857,2859],{"class":2858},"s34zl",":",[1109,2861,1133],{"class":1132},[1109,2863,2864],{"class":1136},"127.0.0.1",[1109,2866,2867],{"class":1132},"'",[1109,2869,1489],{"class":1119},[1109,2871,2872],{"class":1123},"port",[1109,2874,2859],{"class":2858},[1109,2876,2878],{"class":2877},"spgvN"," 10000",[1109,2880,1489],{"class":1119},[1109,2882,2883],{"class":1123},"path",[1109,2885,2859],{"class":2858},[1109,2887,1133],{"class":1132},[1109,2889,2890],{"class":1136},"\u002Fhealth",[1109,2892,2867],{"class":1132},[1109,2894,1489],{"class":1119},[1109,2896,2897],{"class":1123},"timeout",[1109,2899,2859],{"class":2858},[1109,2901,2902],{"class":2877}," 2000",[1109,2904,2905],{"class":1119}," })\n",[856,2907,2908,2909,2911,2912,1308,2915,2918,2919,2921,2922,955],{},"The check hits ",[860,2910,1432],{}," on localhost. A ",[860,2913,2914],{},"2xx",[860,2916,2917],{},"401"," response means the service is healthy. The ",[860,2920,2917],{}," case covers scenarios where HMAC is enabled but the health check runs without headers. Because the HMAC middleware skips localhost health checks, this typically returns ",[860,2923,2924],{},"200",[884,2926],{},[887,2928,2930],{"id":2929},"background-tasks","Background Tasks",[856,2932,2933,2934,2936],{},"After the server starts listening, ",[860,2935,950],{}," schedules three recurring background tasks:",[895,2938,2939,2954],{},[898,2940,2941],{},[901,2942,2943,2946,2949,2952],{},[904,2944,2945],{},"Task",[904,2947,2948],{},"Command",[904,2950,2951],{},"Default Interval",[904,2953,912],{},[914,2955,2956,2972,2988],{},[901,2957,2958,2961,2966,2969],{},[919,2959,2960],{},"Bot detector refresh",[919,2962,2963],{},[860,2964,2965],{},"bot-detector refresh",[919,2967,2968],{},"24 hours",[919,2970,2971],{},"Updates IP reputation, geo, and threat intelligence data sources",[901,2973,2974,2977,2982,2985],{},[919,2975,2976],{},"Bot detector generate",[919,2978,2979],{},[860,2980,2981],{},"bot-detector generate",[919,2983,2984],{},"3 days",[919,2986,2987],{},"Regenerates compiled MMDB databases from updated sources",[901,2989,2990,2993,2998,3001],{},[919,2991,2992],{},"Disposable email list",[919,2994,2995],{},[860,2996,2997],{},"shield-base --email --path=dist",[919,2999,3000],{},"7 days",[919,3002,3003],{},"Updates the LMDB database of known disposable email domains",[856,3005,3006,3007,3010,3011,3014],{},"Each task runs with ",[860,3008,3009],{},"nice -n 19"," (lowest CPU priority) to minimize impact on request handling. Tasks are scheduled with ",[860,3012,3013],{},"setTimeout"," and reschedule themselves after completion, so the interval measures the gap between the end of one run and the start of the next.",[3016,3017,3018],"tip",{},[856,3019,3020],{},"These tasks keep the bot detector's threat intelligence and the signup flow's disposable email list current without requiring a service restart. The first run is deferred by the full interval after startup, so a freshly deployed service uses the data that was baked into the image at build time.",[884,3022],{},[887,3024,3026],{"id":3025},"library-integration","Library Integration",[856,3028,3029,3030,3032,3033,3035,3036,3038],{},"When using ",[860,3031,874],{}," as a library in your own Express application, you skip ",[860,3034,950],{}," and either call ",[860,3037,881],{}," or mount the individual pieces yourself.",[1001,3040,3042,3043],{"id":3041},"using-bootstrapapp","Using ",[860,3044,881],{},[856,3046,3047],{},"The simplest integration. Provides the full IAM service as a sub-application:",[1100,3049,3051],{"className":1102,"code":3050,"language":1104,"meta":1105,"style":1105},"import express from 'express'\nimport { bootstrapApp } from '@riavzon\u002Fauth\u002Fservice'\n\nconst mainApp = express()\n\nconst authApp = await bootstrapApp({\n  store: { main: { host: 'localhost', ... }, rate_limiters_pool: { store: { host: 'localhost', ... }, dbName: 'rate_limiters' } },\n  password: { pepper: 'your-pepper' },\n  botDetector: { enableBotDetector: false },\n  htmlSanitizer: {},\n  magic_links: { jwt_secret_key: '...', domain: 'https:\u002F\u002Fexample.com', notificationEmail: { ... } },\n  jwt: { jwt_secret_key: '...', access_tokens: {}, refresh_tokens: { refresh_ttl: 604800000, domain: 'example.com', MAX_SESSION_LIFE: 2592000000, maxAllowedSessionsPerUser: 5, byPassAnomaliesFor: 86400000 } },\n  email: { resend_key: '...', email: 'noreply@example.com' },\n})\n\nmainApp.use('\u002Fauth', authApp)\nmainApp.listen(3000)\n",[860,3052,3053,3070,3088,3092,3105,3109,3126,3207,3232,3253,3264,3312,3401,3438,3444,3449,3476],{"__ignoreMap":1105},[1109,3054,3055,3057,3060,3063,3065,3068],{"class":1111,"line":1112},[1109,3056,1116],{"class":1115},[1109,3058,3059],{"class":1123}," express",[1109,3061,3062],{"class":1115}," from",[1109,3064,1133],{"class":1132},[1109,3066,3067],{"class":1136},"express",[1109,3069,1139],{"class":1132},[1109,3071,3072,3074,3076,3078,3080,3082,3084,3086],{"class":1111,"line":1142},[1109,3073,1116],{"class":1115},[1109,3075,1120],{"class":1119},[1109,3077,881],{"class":1123},[1109,3079,1126],{"class":1119},[1109,3081,1129],{"class":1115},[1109,3083,1133],{"class":1132},[1109,3085,862],{"class":1136},[1109,3087,1139],{"class":1132},[1109,3089,3090],{"class":1111,"line":1148},[1109,3091,1145],{"emptyLinePlaceholder":8},[1109,3093,3094,3096,3099,3101,3103],{"class":1111,"line":1155},[1109,3095,1253],{"class":1252},[1109,3097,3098],{"class":1256}," mainApp",[1109,3100,1261],{"class":1260},[1109,3102,3059],{"class":1167},[1109,3104,1171],{"class":1119},[1109,3106,3107],{"class":1111,"line":1161},[1109,3108,1145],{"emptyLinePlaceholder":8},[1109,3110,3112,3114,3117,3119,3121,3123],{"class":1111,"line":3111},6,[1109,3113,1253],{"class":1252},[1109,3115,3116],{"class":1256}," authApp",[1109,3118,1261],{"class":1260},[1109,3120,1264],{"class":1115},[1109,3122,1267],{"class":1167},[1109,3124,3125],{"class":1119},"({\n",[1109,3127,3129,3132,3134,3136,3139,3141,3143,3145,3147,3149,3152,3154,3156,3159,3162,3165,3167,3169,3172,3174,3176,3178,3180,3182,3184,3186,3188,3190,3192,3195,3197,3199,3202,3204],{"class":1111,"line":3128},7,[1109,3130,3131],{"class":1123},"  store",[1109,3133,2859],{"class":2858},[1109,3135,1120],{"class":1119},[1109,3137,3138],{"class":1123},"main",[1109,3140,2859],{"class":2858},[1109,3142,1120],{"class":1119},[1109,3144,2855],{"class":1123},[1109,3146,2859],{"class":2858},[1109,3148,1133],{"class":1132},[1109,3150,3151],{"class":1136},"localhost",[1109,3153,2867],{"class":1132},[1109,3155,1489],{"class":1119},[1109,3157,3158],{"class":1260},"...",[1109,3160,3161],{"class":1119}," }, ",[1109,3163,3164],{"class":1123},"rate_limiters_pool",[1109,3166,2859],{"class":2858},[1109,3168,1120],{"class":1119},[1109,3170,3171],{"class":1123},"store",[1109,3173,2859],{"class":2858},[1109,3175,1120],{"class":1119},[1109,3177,2855],{"class":1123},[1109,3179,2859],{"class":2858},[1109,3181,1133],{"class":1132},[1109,3183,3151],{"class":1136},[1109,3185,2867],{"class":1132},[1109,3187,1489],{"class":1119},[1109,3189,3158],{"class":1260},[1109,3191,3161],{"class":1119},[1109,3193,3194],{"class":1123},"dbName",[1109,3196,2859],{"class":2858},[1109,3198,1133],{"class":1132},[1109,3200,3201],{"class":1136},"rate_limiters",[1109,3203,2867],{"class":1132},[1109,3205,3206],{"class":1119}," } },\n",[1109,3208,3210,3213,3215,3217,3220,3222,3224,3227,3229],{"class":1111,"line":3209},8,[1109,3211,3212],{"class":1123},"  password",[1109,3214,2859],{"class":2858},[1109,3216,1120],{"class":1119},[1109,3218,3219],{"class":1123},"pepper",[1109,3221,2859],{"class":2858},[1109,3223,1133],{"class":1132},[1109,3225,3226],{"class":1136},"your-pepper",[1109,3228,2867],{"class":1132},[1109,3230,3231],{"class":1119}," },\n",[1109,3233,3235,3238,3240,3242,3245,3247,3251],{"class":1111,"line":3234},9,[1109,3236,3237],{"class":1123},"  botDetector",[1109,3239,2859],{"class":2858},[1109,3241,1120],{"class":1119},[1109,3243,3244],{"class":1123},"enableBotDetector",[1109,3246,2859],{"class":2858},[1109,3248,3250],{"class":3249},"sjR7W"," false",[1109,3252,3231],{"class":1119},[1109,3254,3256,3259,3261],{"class":1111,"line":3255},10,[1109,3257,3258],{"class":1123},"  htmlSanitizer",[1109,3260,2859],{"class":2858},[1109,3262,3263],{"class":1119}," {},\n",[1109,3265,3267,3270,3272,3274,3277,3279,3281,3283,3285,3287,3290,3292,3294,3297,3299,3301,3304,3306,3308,3310],{"class":1111,"line":3266},11,[1109,3268,3269],{"class":1123},"  magic_links",[1109,3271,2859],{"class":2858},[1109,3273,1120],{"class":1119},[1109,3275,3276],{"class":1123},"jwt_secret_key",[1109,3278,2859],{"class":2858},[1109,3280,1133],{"class":1132},[1109,3282,3158],{"class":1136},[1109,3284,2867],{"class":1132},[1109,3286,1489],{"class":1119},[1109,3288,3289],{"class":1123},"domain",[1109,3291,2859],{"class":2858},[1109,3293,1133],{"class":1132},[1109,3295,3296],{"class":1136},"https:\u002F\u002Fexample.com",[1109,3298,2867],{"class":1132},[1109,3300,1489],{"class":1119},[1109,3302,3303],{"class":1123},"notificationEmail",[1109,3305,2859],{"class":2858},[1109,3307,1120],{"class":1119},[1109,3309,3158],{"class":1260},[1109,3311,3206],{"class":1119},[1109,3313,3315,3318,3320,3322,3324,3326,3328,3330,3332,3334,3337,3339,3342,3344,3346,3348,3351,3353,3356,3358,3360,3362,3364,3367,3369,3371,3374,3376,3379,3381,3384,3386,3389,3391,3394,3396,3399],{"class":1111,"line":3314},12,[1109,3316,3317],{"class":1123},"  jwt",[1109,3319,2859],{"class":2858},[1109,3321,1120],{"class":1119},[1109,3323,3276],{"class":1123},[1109,3325,2859],{"class":2858},[1109,3327,1133],{"class":1132},[1109,3329,3158],{"class":1136},[1109,3331,2867],{"class":1132},[1109,3333,1489],{"class":1119},[1109,3335,3336],{"class":1123},"access_tokens",[1109,3338,2859],{"class":2858},[1109,3340,3341],{"class":1119}," {}, ",[1109,3343,1496],{"class":1123},[1109,3345,2859],{"class":2858},[1109,3347,1120],{"class":1119},[1109,3349,3350],{"class":1123},"refresh_ttl",[1109,3352,2859],{"class":2858},[1109,3354,3355],{"class":2877}," 604800000",[1109,3357,1489],{"class":1119},[1109,3359,3289],{"class":1123},[1109,3361,2859],{"class":2858},[1109,3363,1133],{"class":1132},[1109,3365,3366],{"class":1136},"example.com",[1109,3368,2867],{"class":1132},[1109,3370,1489],{"class":1119},[1109,3372,3373],{"class":1123},"MAX_SESSION_LIFE",[1109,3375,2859],{"class":2858},[1109,3377,3378],{"class":2877}," 2592000000",[1109,3380,1489],{"class":1119},[1109,3382,3383],{"class":1123},"maxAllowedSessionsPerUser",[1109,3385,2859],{"class":2858},[1109,3387,3388],{"class":2877}," 5",[1109,3390,1489],{"class":1119},[1109,3392,3393],{"class":1123},"byPassAnomaliesFor",[1109,3395,2859],{"class":2858},[1109,3397,3398],{"class":2877}," 86400000",[1109,3400,3206],{"class":1119},[1109,3402,3404,3407,3409,3411,3414,3416,3418,3420,3422,3424,3427,3429,3431,3434,3436],{"class":1111,"line":3403},13,[1109,3405,3406],{"class":1123},"  email",[1109,3408,2859],{"class":2858},[1109,3410,1120],{"class":1119},[1109,3412,3413],{"class":1123},"resend_key",[1109,3415,2859],{"class":2858},[1109,3417,1133],{"class":1132},[1109,3419,3158],{"class":1136},[1109,3421,2867],{"class":1132},[1109,3423,1489],{"class":1119},[1109,3425,3426],{"class":1123},"email",[1109,3428,2859],{"class":2858},[1109,3430,1133],{"class":1132},[1109,3432,3433],{"class":1136},"noreply@example.com",[1109,3435,2867],{"class":1132},[1109,3437,3231],{"class":1119},[1109,3439,3441],{"class":1111,"line":3440},14,[1109,3442,3443],{"class":1119},"})\n",[1109,3445,3447],{"class":1111,"line":3446},15,[1109,3448,1145],{"emptyLinePlaceholder":8},[1109,3450,3452,3455,3457,3460,3462,3464,3467,3469,3471,3474],{"class":1111,"line":3451},16,[1109,3453,3454],{"class":1123},"mainApp",[1109,3456,955],{"class":1119},[1109,3458,3459],{"class":1167},"use",[1109,3461,1270],{"class":1119},[1109,3463,2867],{"class":1132},[1109,3465,3466],{"class":1136},"\u002Fauth",[1109,3468,2867],{"class":1132},[1109,3470,1489],{"class":1119},[1109,3472,3473],{"class":1123},"authApp",[1109,3475,1276],{"class":1119},[1109,3477,3479,3481,3483,3486,3488,3491],{"class":1111,"line":3478},17,[1109,3480,3454],{"class":1123},[1109,3482,955],{"class":1119},[1109,3484,3485],{"class":1167},"listen",[1109,3487,1270],{"class":1119},[1109,3489,3490],{"class":2877},"3000",[1109,3492,1276],{"class":1119},[1173,3494,3495],{},[856,3496,3497,3498,3500,3501,3503],{},"When mounting the auth app under a prefix like ",[860,3499,3466],{},", remember that the internal routes already include their own prefixes (e.g., ",[860,3502,2063],{},"). Adjust your mount point to avoid doubled path segments.",[1001,3505,3507],{"id":3506},"using-individual-routers","Using Individual Routers",[856,3509,3510,3511,3514],{},"For more control, call ",[860,3512,3513],{},"configuration()"," to initialize the pools and config, then mount only the routers you need:",[1100,3516,3518],{"className":1102,"code":3517,"language":1104,"meta":1105,"style":1105},"import express from 'express'\nimport cookieParser from 'cookie-parser'\nimport {\n  configuration,\n  authenticationRoutes,\n  tokenRotationRoutes,\n  magicLinks,\n  bffAccessRoute,\n  apiVerificationRoute,\n  apiProtectedRoutes,\n} from '@riavzon\u002Fauth'\n\nconst app = express()\n\nawait configuration({\n  store: { ... },\n  password: { pepper: '...' },\n  \u002F\u002F ... rest of config\n})\n\n\u002F\u002F Mount only what you need\napp.use(apiVerificationRoute())\napp.use(cookieParser())\napp.use(authenticationRoutes)\napp.use(tokenRotationRoutes)\napp.use(magicLinks)\napp.use(bffAccessRoute)\napp.use(apiProtectedRoutes())\n\napp.listen(3000)\n",[860,3519,3520,3534,3550,3557,3564,3571,3578,3585,3592,3599,3606,3619,3623,3635,3639,3648,3660,3680,3686,3691,3696,3702,3720,3736,3752,3768,3784,3800,3816,3821],{"__ignoreMap":1105},[1109,3521,3522,3524,3526,3528,3530,3532],{"class":1111,"line":1112},[1109,3523,1116],{"class":1115},[1109,3525,3059],{"class":1123},[1109,3527,3062],{"class":1115},[1109,3529,1133],{"class":1132},[1109,3531,3067],{"class":1136},[1109,3533,1139],{"class":1132},[1109,3535,3536,3538,3541,3543,3545,3548],{"class":1111,"line":1142},[1109,3537,1116],{"class":1115},[1109,3539,3540],{"class":1123}," cookieParser",[1109,3542,3062],{"class":1115},[1109,3544,1133],{"class":1132},[1109,3546,3547],{"class":1136},"cookie-parser",[1109,3549,1139],{"class":1132},[1109,3551,3552,3554],{"class":1111,"line":1148},[1109,3553,1116],{"class":1115},[1109,3555,3556],{"class":1119}," {\n",[1109,3558,3559,3562],{"class":1111,"line":1155},[1109,3560,3561],{"class":1123},"  configuration",[1109,3563,1493],{"class":1119},[1109,3565,3566,3569],{"class":1111,"line":1161},[1109,3567,3568],{"class":1123},"  authenticationRoutes",[1109,3570,1493],{"class":1119},[1109,3572,3573,3576],{"class":1111,"line":3111},[1109,3574,3575],{"class":1123},"  tokenRotationRoutes",[1109,3577,1493],{"class":1119},[1109,3579,3580,3583],{"class":1111,"line":3128},[1109,3581,3582],{"class":1123},"  magicLinks",[1109,3584,1493],{"class":1119},[1109,3586,3587,3590],{"class":1111,"line":3209},[1109,3588,3589],{"class":1123},"  bffAccessRoute",[1109,3591,1493],{"class":1119},[1109,3593,3594,3597],{"class":1111,"line":3234},[1109,3595,3596],{"class":1123},"  apiVerificationRoute",[1109,3598,1493],{"class":1119},[1109,3600,3601,3604],{"class":1111,"line":3255},[1109,3602,3603],{"class":1123},"  apiProtectedRoutes",[1109,3605,1493],{"class":1119},[1109,3607,3608,3611,3613,3615,3617],{"class":1111,"line":3266},[1109,3609,3610],{"class":1119},"} ",[1109,3612,1129],{"class":1115},[1109,3614,1133],{"class":1132},[1109,3616,874],{"class":1136},[1109,3618,1139],{"class":1132},[1109,3620,3621],{"class":1111,"line":3314},[1109,3622,1145],{"emptyLinePlaceholder":8},[1109,3624,3625,3627,3629,3631,3633],{"class":1111,"line":3403},[1109,3626,1253],{"class":1252},[1109,3628,1257],{"class":1256},[1109,3630,1261],{"class":1260},[1109,3632,3059],{"class":1167},[1109,3634,1171],{"class":1119},[1109,3636,3637],{"class":1111,"line":3440},[1109,3638,1145],{"emptyLinePlaceholder":8},[1109,3640,3641,3643,3646],{"class":1111,"line":3446},[1109,3642,1164],{"class":1115},[1109,3644,3645],{"class":1167}," configuration",[1109,3647,3125],{"class":1119},[1109,3649,3650,3652,3654,3656,3658],{"class":1111,"line":3451},[1109,3651,3131],{"class":1123},[1109,3653,2859],{"class":2858},[1109,3655,1120],{"class":1119},[1109,3657,3158],{"class":1260},[1109,3659,3231],{"class":1119},[1109,3661,3662,3664,3666,3668,3670,3672,3674,3676,3678],{"class":1111,"line":3478},[1109,3663,3212],{"class":1123},[1109,3665,2859],{"class":2858},[1109,3667,1120],{"class":1119},[1109,3669,3219],{"class":1123},[1109,3671,2859],{"class":2858},[1109,3673,1133],{"class":1132},[1109,3675,3158],{"class":1136},[1109,3677,2867],{"class":1132},[1109,3679,3231],{"class":1119},[1109,3681,3683],{"class":1111,"line":3682},18,[1109,3684,3685],{"class":1151},"  \u002F\u002F ... rest of config\n",[1109,3687,3689],{"class":1111,"line":3688},19,[1109,3690,3443],{"class":1119},[1109,3692,3694],{"class":1111,"line":3693},20,[1109,3695,1145],{"emptyLinePlaceholder":8},[1109,3697,3699],{"class":1111,"line":3698},21,[1109,3700,3701],{"class":1151},"\u002F\u002F Mount only what you need\n",[1109,3703,3705,3708,3710,3712,3714,3717],{"class":1111,"line":3704},22,[1109,3706,3707],{"class":1123},"app",[1109,3709,955],{"class":1119},[1109,3711,3459],{"class":1167},[1109,3713,1270],{"class":1119},[1109,3715,3716],{"class":1167},"apiVerificationRoute",[1109,3718,3719],{"class":1119},"())\n",[1109,3721,3723,3725,3727,3729,3731,3734],{"class":1111,"line":3722},23,[1109,3724,3707],{"class":1123},[1109,3726,955],{"class":1119},[1109,3728,3459],{"class":1167},[1109,3730,1270],{"class":1119},[1109,3732,3733],{"class":1167},"cookieParser",[1109,3735,3719],{"class":1119},[1109,3737,3739,3741,3743,3745,3747,3750],{"class":1111,"line":3738},24,[1109,3740,3707],{"class":1123},[1109,3742,955],{"class":1119},[1109,3744,3459],{"class":1167},[1109,3746,1270],{"class":1119},[1109,3748,3749],{"class":1123},"authenticationRoutes",[1109,3751,1276],{"class":1119},[1109,3753,3755,3757,3759,3761,3763,3766],{"class":1111,"line":3754},25,[1109,3756,3707],{"class":1123},[1109,3758,955],{"class":1119},[1109,3760,3459],{"class":1167},[1109,3762,1270],{"class":1119},[1109,3764,3765],{"class":1123},"tokenRotationRoutes",[1109,3767,1276],{"class":1119},[1109,3769,3771,3773,3775,3777,3779,3782],{"class":1111,"line":3770},26,[1109,3772,3707],{"class":1123},[1109,3774,955],{"class":1119},[1109,3776,3459],{"class":1167},[1109,3778,1270],{"class":1119},[1109,3780,3781],{"class":1123},"magicLinks",[1109,3783,1276],{"class":1119},[1109,3785,3787,3789,3791,3793,3795,3798],{"class":1111,"line":3786},27,[1109,3788,3707],{"class":1123},[1109,3790,955],{"class":1119},[1109,3792,3459],{"class":1167},[1109,3794,1270],{"class":1119},[1109,3796,3797],{"class":1123},"bffAccessRoute",[1109,3799,1276],{"class":1119},[1109,3801,3803,3805,3807,3809,3811,3814],{"class":1111,"line":3802},28,[1109,3804,3707],{"class":1123},[1109,3806,955],{"class":1119},[1109,3808,3459],{"class":1167},[1109,3810,1270],{"class":1119},[1109,3812,3813],{"class":1167},"apiProtectedRoutes",[1109,3815,3719],{"class":1119},[1109,3817,3819],{"class":1111,"line":3818},29,[1109,3820,1145],{"emptyLinePlaceholder":8},[1109,3822,3824,3826,3828,3830,3832,3834],{"class":1111,"line":3823},30,[1109,3825,3707],{"class":1123},[1109,3827,955],{"class":1119},[1109,3829,3485],{"class":1167},[1109,3831,1270],{"class":1119},[1109,3833,3490],{"class":2877},[1109,3835,1276],{"class":1119},[1173,3837,3838],{},[856,3839,3840,3841,3843,3844,3846,3847,3849,3850,3852,3853,1489,3856,2513,3859,955],{},"You must call ",[860,3842,3513],{}," before mounting any routers. The routers and\ncontrollers call ",[860,3845,1387],{}," internally, which throws if the config\nhas not been initialized. Register ",[860,3848,1797],{}," before any router that\nreads ",[860,3851,1807],{},". The same applies to ",[860,3854,3855],{},"getPool()",[860,3857,3858],{},"poolForLibrary()",[860,3860,3861],{},"getDisposableEmailList()",[1001,3863,3865],{"id":3864},"cli-initialization","CLI Initialization",[856,3867,3868,3869,3872,3873,3876],{},"The package also exposes a CLI binary (",[860,3870,3871],{},"auth",") and an ",[860,3874,3875],{},"initAuthData"," function for one-time setup tasks like creating database tables and downloading the disposable email list:",[1100,3878,3880],{"className":1102,"code":3879,"language":1104,"meta":1105,"style":1105},"import { initAuthData } from '@riavzon\u002Fauth'\n\nawait initAuthData(config)\n",[860,3881,3882,3900,3904],{"__ignoreMap":1105},[1109,3883,3884,3886,3888,3890,3892,3894,3896,3898],{"class":1111,"line":1112},[1109,3885,1116],{"class":1115},[1109,3887,1120],{"class":1119},[1109,3889,3875],{"class":1123},[1109,3891,1126],{"class":1119},[1109,3893,1129],{"class":1115},[1109,3895,1133],{"class":1132},[1109,3897,874],{"class":1136},[1109,3899,1139],{"class":1132},[1109,3901,3902],{"class":1111,"line":1142},[1109,3903,1145],{"emptyLinePlaceholder":8},[1109,3905,3906,3908,3911,3913,3915],{"class":1111,"line":1148},[1109,3907,1164],{"class":1115},[1109,3909,3910],{"class":1167}," initAuthData",[1109,3912,1270],{"class":1119},[1109,3914,1273],{"class":1123},[1109,3916,1276],{"class":1119},[856,3918,3919],{},"Or from the command line:",[1100,3921,3925],{"className":3922,"code":3923,"language":3924,"meta":1105,"style":1105},"language-bash shiki shiki-themes light-plus light-plus dracula","npx auth .\u002Fconfig.json\n","bash",[860,3926,3927],{"__ignoreMap":1105},[1109,3928,3929,3932,3935],{"class":1111,"line":1112},[1109,3930,3931],{"class":1167},"npx",[1109,3933,3934],{"class":1136}," auth",[1109,3936,3937],{"class":1136}," .\u002Fconfig.json\n",[856,3939,3940,3941,3943,3944,1489,3946,3949],{},"This runs ",[860,3942,3513],{},", bot-detector setup, ",[860,3945,1510],{},[860,3947,3948],{},"createTables()",", and the disposable email LMDB download in sequence.",[884,3951],{},[887,3953,3955],{"id":3954},"complete-route-map","Complete Route Map",[856,3957,3958],{},"For reference, here is every route the IAM service registers:",[895,3960,3961,3976],{},[898,3962,3963],{},[901,3964,3965,3967,3969,3972,3974],{},[904,3966,1860],{},[904,3968,1863],{},[904,3970,3971],{},"Auth Required",[904,3973,2596],{},[904,3975,1586],{},[914,3977,3978,3994,4012,4028,4045,4063,4080,4097,4113,4130,4146,4162,4178,4194,4210,4226,4242,4258],{},[901,3979,3980,3982,3986,3989,3992],{},[919,3981,1874],{},[919,3983,3984],{},[860,3985,2890],{},[919,3987,3988],{},"No",[919,3990,3991],{},"None",[919,3993],{},[901,3995,3996,3998,4002,4005,4008],{},[919,3997,1932],{},[919,3999,4000],{},[860,4001,1937],{},[919,4003,4004],{},"canary cookie",[919,4006,4007],{},"JSON (1 KB)",[919,4009,4010],{},[865,4011,99],{"href":100},[901,4013,4014,4016,4020,4022,4024],{},[919,4015,1932],{},[919,4017,4018],{},[860,4019,1962],{},[919,4021,4004],{},[919,4023,4007],{},[919,4025,4026],{},[865,4027,103],{"href":104},[901,4029,4030,4032,4036,4038,4041],{},[919,4031,1932],{},[919,4033,4034],{},[860,4035,1984],{},[919,4037,4004],{},[919,4039,4040],{},"JSON (4 KB)",[919,4042,4043],{},[865,4044,111],{"href":112},[901,4046,4047,4049,4053,4056,4059],{},[919,4048,1932],{},[919,4050,4051],{},[860,4052,2063],{},[919,4054,4055],{},"Refresh cookie",[919,4057,4058],{},"None (cookie only)",[919,4060,4061],{},[865,4062,91],{"href":92},[901,4064,4065,4067,4071,4074,4076],{},[919,4066,1932],{},[919,4068,4069],{},[860,4070,2093],{},[919,4072,4073],{},"Access + Refresh",[919,4075,4058],{},[919,4077,4078],{},[865,4079,107],{"href":108},[901,4081,4082,4084,4088,4091,4093],{},[919,4083,1874],{},[919,4085,4086],{},[860,4087,2150],{},[919,4089,4090],{},"Magic link token",[919,4092,3991],{},[919,4094,4095],{},[865,4096,123],{"href":124},[901,4098,4099,4101,4105,4107,4109],{},[919,4100,1932],{},[919,4102,4103],{},[860,4104,2150],{},[919,4106,4090],{},[919,4108,4007],{},[919,4110,4111],{},[865,4112,123],{"href":124},[901,4114,4115,4117,4121,4124,4126],{},[919,4116,1932],{},[919,4118,4119],{},[860,4120,2181],{},[919,4122,4123],{},"Access + Refresh + JWT",[919,4125,4007],{},[919,4127,4128],{},[865,4129,123],{"href":124},[901,4131,4132,4134,4138,4140,4142],{},[919,4133,1874],{},[919,4135,4136],{},[860,4137,2197],{},[919,4139,4123],{},[919,4141,3991],{},[919,4143,4144],{},[865,4145,123],{"href":124},[901,4147,4148,4150,4154,4156,4158],{},[919,4149,1932],{},[919,4151,4152],{},[860,4153,2197],{},[919,4155,4123],{},[919,4157,4007],{},[919,4159,4160],{},[865,4161,123],{"href":124},[901,4163,4164,4166,4170,4172,4174],{},[919,4165,1932],{},[919,4167,4168],{},[860,4169,2228],{},[919,4171,4123],{},[919,4173,4007],{},[919,4175,4176],{},[865,4177,123],{"href":2236},[901,4179,4180,4182,4186,4188,4190],{},[919,4181,1932],{},[919,4183,4184],{},[860,4185,2245],{},[919,4187,3991],{},[919,4189,4007],{},[919,4191,4192],{},[865,4193,163],{"href":164},[901,4195,4196,4198,4202,4204,4206],{},[919,4197,1874],{},[919,4199,4200],{},[860,4201,2261],{},[919,4203,4090],{},[919,4205,3991],{},[919,4207,4208],{},[865,4209,163],{"href":164},[901,4211,4212,4214,4218,4220,4222],{},[919,4213,1932],{},[919,4215,4216],{},[860,4217,2261],{},[919,4219,4090],{},[919,4221,4007],{},[919,4223,4224],{},[865,4225,163],{"href":164},[901,4227,4228,4230,4234,4236,4238],{},[919,4229,1874],{},[919,4231,4232],{},[860,4233,2329],{},[919,4235,4123],{},[919,4237,3991],{},[919,4239,4240],{},[865,4241,2337],{"href":132},[901,4243,4244,4246,4250,4252,4254],{},[919,4245,1874],{},[919,4247,4248],{},[860,4249,2346],{},[919,4251,4123],{},[919,4253,3991],{},[919,4255,4256],{},[865,4257,2337],{"href":132},[901,4259,4260,4262,4266,4269,4271],{},[919,4261,1874],{},[919,4263,4264],{},[860,4265,2556],{},[919,4267,4268],{},"Trusted IP only",[919,4270,3991],{},[919,4272],{},[884,4274],{},[887,4276,4278],{"id":4277},"summary","Summary",[895,4280,4281,4291],{},[898,4282,4283],{},[901,4284,4285,4288],{},[904,4286,4287],{},"Concept",[904,4289,4290],{},"Description",[914,4292,4293,4308,4322,4333,4343,4353,4363,4373,4383],{},[901,4294,4295,4300],{},[919,4296,4297],{},[1337,4298,4299],{},"Two entry points",[919,4301,4302,4304,4305,4307],{},[860,4303,874],{}," for library usage, ",[860,4306,862],{}," for standalone deployment",[901,4309,4310,4316],{},[919,4311,4312],{},[1337,4313,4314],{},[860,4315,950],{},[919,4317,4318,4319,4321],{},"Reads encrypted config, calls ",[860,4320,881],{},", deletes config from disk, listens on port",[901,4323,4324,4330],{},[919,4325,4326],{},[1337,4327,4328],{},[860,4329,881],{},[919,4331,4332],{},"Initializes pools, tables, middleware, routes. Returns Express app",[901,4334,4335,4340],{},[919,4336,4337],{},[1337,4338,4339],{},"Config lifecycle",[919,4341,4342],{},"Decrypted at container start, parsed at boot, deleted from disk, frozen in memory",[901,4344,4345,4350],{},[919,4346,4347],{},[1337,4348,4349],{},"Middleware order",[919,4351,4352],{},"Logger, helmet, cache headers, IP validation, HMAC (optional), JSON parser, cookie parser, bot detector",[901,4354,4355,4360],{},[919,4356,4357],{},[1337,4358,4359],{},"Route groups",[919,4361,4362],{},"Authentication, token rotation, magic links, BFF access, operational config",[901,4364,4365,4370],{},[919,4366,4367],{},[1337,4368,4369],{},"Background tasks",[919,4371,4372],{},"Bot detector data refresh (daily), database generation (3 days), disposable email update (weekly)",[901,4374,4375,4380],{},[919,4376,4377],{},[1337,4378,4379],{},"Docker security",[919,4381,4382],{},"Read-only filesystem, no capabilities, non-root user, tmpfs for secrets, PID limits",[901,4384,4385,4390],{},[919,4386,4387],{},[1337,4388,4389],{},"Health check",[919,4391,4392,4394,4395,4397],{},[860,4393,1432],{}," returns ",[860,4396,1436],{},", polled every 30 seconds by Docker",[4399,4400,4401],"style",{},"html pre.shiki code .sZ328, html code.shiki .sZ328{--shiki-light:#AF00DB;--shiki-default:#AF00DB;--shiki-dark:#FF79C6}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 .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 .sghk6, html code.shiki .sghk6{--shiki-light:#008000;--shiki-default:#008000;--shiki-dark:#6272A4}html pre.shiki code .sHOzp, html code.shiki .sHOzp{--shiki-light:#795E26;--shiki-default:#795E26;--shiki-dark:#50FA7B}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 .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 .s34zl, html code.shiki .s34zl{--shiki-light:#001080;--shiki-default:#001080;--shiki-dark:#FF79C6}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}",{"title":1105,"searchDepth":1142,"depth":1142,"links":4403},[4404,4405,4408,4411,4412,4421,4422,4423,4428,4429,4435,4436],{"id":889,"depth":1142,"text":890},{"id":991,"depth":1142,"text":950,"children":4406},[4407],{"id":1003,"depth":1148,"text":1004},{"id":1186,"depth":1142,"text":881,"children":4409},[4410],{"id":1279,"depth":1148,"text":1280},{"id":1565,"depth":1142,"text":1566},{"id":1840,"depth":1142,"text":1841,"children":4413},[4414,4415,4416,4417,4418,4419,4420],{"id":1847,"depth":1148,"text":1848},{"id":1905,"depth":1148,"text":1906},{"id":2032,"depth":1148,"text":2033},{"id":2121,"depth":1148,"text":2122},{"id":2300,"depth":1148,"text":2301},{"id":2369,"depth":1148,"text":2370},{"id":2529,"depth":1148,"text":2530},{"id":2577,"depth":1142,"text":2578},{"id":2644,"depth":1142,"text":237},{"id":2657,"depth":1142,"text":2658,"children":4424},[4425,4426,4427],{"id":2664,"depth":1148,"text":2665},{"id":2757,"depth":1148,"text":2758},{"id":2828,"depth":1148,"text":2829},{"id":2929,"depth":1142,"text":2930},{"id":3025,"depth":1142,"text":3026,"children":4430},[4431,4433,4434],{"id":3041,"depth":1148,"text":4432},"Using bootstrapApp",{"id":3506,"depth":1148,"text":3507},{"id":3864,"depth":1148,"text":3865},{"id":3954,"depth":1142,"text":3955},{"id":4277,"depth":1142,"text":4278},"How the IAM service boots, initializes pools and databases, mounts middleware and routes, and serves requests, covering both standalone deployment and library integration.","md",{},null,"---\ntitle: Service Startup\ndescription: How the IAM service boots, initializes pools and databases, mounts middleware and routes, and serves requests, covering both standalone deployment and library integration.\nicon: i-lucide-rocket\n---\n\nThe IAM service ships two entry points. The `@riavzon\u002Fauth\u002Fservice` export provides a complete standalone server that reads an [age](https:\u002F\u002Fgithub.com\u002FFiloSottile\u002Fage)-encrypted configuration file, creates the Express application, and begins listening for requests. The `@riavzon\u002Fauth` export provides the same building blocks as individual functions and routers so you can mount them into your own Express application.\n\nBoth paths converge on the same core function: `bootstrapApp`. This function takes a validated configuration object, creates every pool, table, middleware, and route the service needs, and returns a ready to use Express app.\n\n---\n\n## Package Exports\n\nThe package exposes two subpath exports:\n\n| Export | Entry | Purpose |\n|---|---|---|\n| `@riavzon\u002Fauth` | `dist\u002Fmain.mjs` | Library mode. Individual functions, middleware, routers, and utilities. |\n| `@riavzon\u002Fauth\u002Fservice` | `dist\u002Fservice.mjs` | Standalone mode. Exports `bootstrapApp` and `startServer`. Auto-starts when `NODE_ENV !== 'test'`. |\n\nWhen you import from `@riavzon\u002Fauth`, nothing boots automatically. When you import from `@riavzon\u002Fauth\u002Fservice`, the module calls `startServer()` immediately unless `NODE_ENV` is `test`. In production, this is the module you point your container's `CMD` at.\n\nFor a complete list of every export, see the [API reference](\u002Fdocs\u002Fiam\u002Fapi\u002Fapi).\n\n---\n\n## `startServer`\n\nThe `startServer` function is the outermost entry point in standalone mode. It handles configuration loading, application bootstrap, config file cleanup, and HTTP listening.\n\n### Startup Flow\n\n::steps{level=\"4\"}\n#### Locate the configuration file\nThe function reads the `CONFIG_PATH` environment variable. If the variable is not set, it defaults to `\u002Frun\u002Fapp\u002Fconfig.json`. It then checks that the file exists using `fs.access`. If the file is missing, the process exits with a fatal error.\n\n#### Parse configuration\nThe file is read as UTF-8 and parsed with `JSON.parse`. The resulting object is passed directly to `bootstrapApp`, which validates it against the Zod schema internally. See the [Configuration reference](\u002Fdocs\u002Fiam\u002Fconfiguration) for the full schema.\n\n#### Bootstrap the application\nCalls `bootstrapApp(config)`, which initializes all pools, tables, middleware, and routes. Returns a fully configured Express app.\n\n#### Delete the configuration file\nAfter bootstrapping, the function deletes the configuration file from disk with `fs.unlink`. This ensures that sensitive credentials (database passwords, JWT secrets, HMAC keys, API keys) do not persist on the filesystem after they have been loaded into memory. If the `SKIP_CONFIG_UNLINK` environment variable is set to `'true'`, the file is kept.\n\n#### Start listening\nThe app begins listening on the configured `service.port` (default `10000`) and `service.ipAddress` (default `0.0.0.0`). Once the server is ready, background data-refresh tasks are scheduled.\n::\n\n```ts\nimport { startServer } from '@riavzon\u002Fauth\u002Fservice'\n\n\u002F\u002F In production, this runs automatically.\n\u002F\u002F In tests (NODE_ENV=test), you call it manually:\nawait startServer()\n```\n\n::warning\nThe config file is deleted after parsing. If the process crashes during bootstrap and the file has already been unlinked, you need to re-decrypt it before restarting. In a Docker deployment this happens automatically because `decrypt.sh` runs on every container start.\n::\n\n---\n\n## `bootstrapApp`\n\nThe core bootstrap function. It accepts a `ConfigurationInput` object, performs all initialization, and returns an Express `Application`. Both the standalone server and library consumers use this function.\n\n```ts\nimport { bootstrapApp } from '@riavzon\u002Fauth\u002Fservice'\nimport type { ConfigurationInput } from '@riavzon\u002Fauth'\n\nconst app = await bootstrapApp(config)\n```\n\n### Bootstrap Sequence\n\nThe function executes the following steps in order:\n\n::steps{level=\"4\"}\n#### Configure OAuth providers\nIf the configuration includes a `providers` array, each entry is instantiated as an `OAuthProvider` object. Providers defined with a `schema` field use that Zod schema for profile validation. Providers defined with `fields` or `useStandardProfile` use the built-in `StandardProfileSchema`. See [OAuth](\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth) for provider configuration details.\n\n#### Initialize the configuration manager\nCalls `configuration(config)`, which passes the raw config through the Zod schema for validation, then runs three initialization tasks concurrently:\n\n1. **Main MySQL pool** - Creates a `mysql2\u002Fpromise` connection pool from `config.store.main`. This pool handles all authentication queries (users, refresh tokens, MFA codes).\n2. **Rate limiter pool** - Creates a callback-based `mysql2` connection pool from `config.store.rate_limiters_pool.store`. This separate pool is used exclusively by [rate-limiter-flexible](\u002Fdocs\u002Fiam\u002Fessentials\u002Frate-limiting) for counter storage.\n3. **Disposable email database** - Opens an [LMDB](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLightning_Memory-Mapped_Database) read-only database containing known disposable email domains. Used by the signup flow to reject temporary email addresses.\n\nOnce all three tasks complete, the configuration is frozen with `Object.freeze` and stored as a module-level singleton. Any subsequent call to `getConfiguration()` returns this frozen object.\n\n#### Create the Express application\nCreates a new Express app and conditionally configures [trust proxy](https:\u002F\u002Fexpressjs.com\u002Fen\u002Fguide\u002Fbehind-proxies.html). When `service.proxy.trust` is `true`, the app trusts the configured proxy IP (`service.proxy.server` or `service.ipAddress`) and the client-facing proxy IP (`service.proxy.ipToTrust`). This ensures `req.ip` reflects the real client IP rather than the proxy's address.\n\n#### Register the health endpoint\nMounts `GET \u002Fhealth` before any middleware. The handler returns `200 OK` as plain text. Because it is registered before HMAC authentication, it is accessible without credentials. The Docker health check (`healthcheck.js`) polls this endpoint every 30 seconds.\n\n#### Configure the bot detector\nInitializes the [`@riavzon\u002Fbot-detector`](\u002Fdocs\u002Fbot-detection) module. If the configuration has `botDetector.enableBotDetector: true` with custom `settings`, those settings are used. Otherwise, a default configuration is applied. If bot detection is disabled (`enableBotDetector: false`), a passive-mode configuration is loaded that disables all active checks but still provides the `ApiResponse` middleware for visitor tracking.\n\n#### Create database tables\nCreates the bot-detector tables (via `createTables`) and the IAM tables (via\n`makeTables`). The IAM schema includes four tables: `users`, `api_tokens`,\n`refresh_tokens`, and `mfa_codes`. All table creation statements use\n`CREATE TABLE IF NOT EXISTS`, so repeated calls are safe. After creating\n`api_tokens`, `makeTables()` also ensures the three API-token indexes exist and\nignores duplicate-key errors on repeated runs. See\n[Database](\u002Fdocs\u002Fiam\u002Fessentials\u002Fdatabase) for the full schema.\n\n#### Mount middleware stack\nThe middleware is registered in a specific order. Each layer depends on the layers before it.\n\n#### Mount route groups\nThe public API verification router is registered first. After that, the\nauthenticated route groups are mounted in order: authentication routes, token\nrotation routes, magic link routes, BFF access routes, and API token\nmanagement routes. The standalone operational config endpoint is mounted after\nthose routers.\n\n#### Warm up the bot detector\nCalls `warmUp()` from the bot-detector module to preload geo, ASN, and threat databases into memory. This runs after route registration so that the first incoming request does not pay the cold-start penalty.\n\n#### Register terminal handlers\nMounts the `notFoundHandler` (returns `404` for unmatched paths) and `finalUnHandledErrors` (catches unhandled errors and returns `500`). These are registered last so they only trigger when no route or middleware handled the request.\n::\n\n---\n\n## Middleware Stack\n\nThe middleware is mounted in a fixed order. Every request passes through each layer sequentially:\n\n| Order | Middleware | Purpose | Reference |\n|---|---|---|---|\n| 1 | `httpLogger` | Structured request logging via [pino-http](https:\u002F\u002Fgithub.com\u002Fpinojs\u002Fpino-http). Assigns a unique `X-Request-Id` header to every request. Redacts `Authorization` headers and session cookies from logs. | [Logging](\u002Fdocs\u002Fiam\u002Fessentials\u002Flogging) |\n| 2 | `x-powered-by` disabled | Removes the default Express `X-Powered-By` header to avoid fingerprinting the server technology. | |\n| 3 | `helmet` | Sets security headers: `X-Frame-Options: DENY`, `Content-Security-Policy: frame-ancestors 'none'`, `Cross-Origin-Embedder-Policy`, `Referrer-Policy: origin`. | |\n| 4 | `headers` | Sets `Cache-Control: no-cache, private, max-age=0`, `Pragma: no-cache`, `Expires: 0` on every response. Prevents browsers and proxies from caching sensitive authentication responses. | |\n| 5 | `validateIp` | Validates that `req.ip` is a valid IP address using Node.js `net.isIP()`. Returns `403 Forbidden` if the IP is missing or invalid. | |\n| 6 | `hmacAuth` (conditional) | Only mounted when `service.Hmac` is configured. Verifies the `X-Client-Id`, `X-Timestamp`, `X-Signature`, and `X-Request-ID` headers against a shared secret. Rejects requests with missing headers, unknown client IDs, expired timestamps, replayed nonces, or invalid signatures. Bypasses the health endpoint on localhost. | [HMAC](\u002Fdocs\u002Fiam\u002Fessentials\u002Fhmac) |\n| 7 | `apiVerificationRoute()` | Mounts `GET \u002Fapi\u002Fpublic\u002Fverify` before body and cookie parsing. | [API Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification) |\n| 8 | `express.json()` | Parses JSON request bodies. | |\n| 9 | `cookieParser()` | Parses `Cookie` headers into `req.cookies`. | [Cookies](\u002Fdocs\u002Fiam\u002Fessentials\u002Fcookies) |\n| 10 | `ApiResponse` | Bot-detector middleware that attaches visitor data and geo information to every request. | [Bot Detection](\u002Fdocs\u002Fbot-detection) |\n\n::warning\nThe middleware and route order is security-critical. IP validation runs before\nHMAC so that requests with spoofed or missing IPs are rejected before\nsignature verification begins. The public API verification route is mounted\nbefore body parsing and cookie parsing because it does not need either. The\n`ApiResponse` middleware is last because it relies on cookies being parsed.\n::\n\n---\n\n## Route Groups\n\nAfter the early health check and service-wide middleware, six route groups are\nmounted. Five are authenticated or semi-public routers, and the operational\nconfig endpoint is mounted afterward as a direct handler.\n\n### Public API Verification Route\n\nHandles machine-to-machine API token verification.\n\n| Method | Path | Purpose | Reference |\n|---|---|---|---|\n| GET | `\u002Fapi\u002Fpublic\u002Fverify` | Verify an API token against a required privilege | [API Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fverification) |\n\nThis route is mounted before `express.json()`, `cookieParser()`, and the\nbot-detector middleware. It reads the token from the `x-api-key` header and the\nrequired privilege from the `privilege` query parameter.\n\n### Authentication Routes\n\nHandles account creation, login, and OAuth flows.\n\n| Method | Path | Purpose | Middleware | Reference |\n|---|---|---|---|---|\n| POST | `\u002Fsignup` | Create a new account | `contentType('application\u002Fjson')`, `json({ limit: '1kb' })`, canary cookie check | [Signup](\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup) |\n| POST | `\u002Flogin` | Authenticate with email and password | `contentType('application\u002Fjson')`, `json({ limit: '1kb' })`, canary cookie check | [Login](\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin) |\n| POST | `\u002Fauth\u002FOAuth\u002F:providerName` | Authenticate via an OAuth provider | `contentType('application\u002Fjson')`, `json({ limit: '4kb' })`, canary cookie check | [OAuth](\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth) |\n\nAll three routes enforce a `Content-Type: application\u002Fjson` header via the `contentType` middleware. The `json()` middleware limits request bodies to 1 KB (4 KB for OAuth) and rejects empty bodies with a `403`. Each route also requires the `canary_id` cookie from the [bot-detector](\u002Fdocs\u002Fbot-detection) and returns `400` if it is missing.\n\n### Token Rotation Routes\n\nHandles token refresh and logout.\n\n| Method | Path | Purpose | Middleware | Reference |\n|---|---|---|---|---|\n| POST | `\u002Fauth\u002Fuser\u002Frefresh-session` | Rotate both access and refresh tokens | `requireRefreshToken`, `acceptCookieOnly`, `getFingerPrint`, `checkForActiveMfa` | [Refresh Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens) |\n| POST | `\u002Fauth\u002Flogout` | Revoke the current session | `requireRefreshToken`, `requireAccessToken`, `acceptCookieOnly` | [Logout](\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout) |\n\nThe `acceptCookieOnly` middleware enforces that these requests send only cookies: no JSON body, no query string, and no `Content-Type` header. This prevents CSRF attacks by ensuring the request cannot carry attacker-controlled data beyond the browser's automatic cookie attachment.\n\n### Magic Link Routes\n\nHandles MFA verification, password reset, custom MFA flows, and email changes.\n\n| Method | Path | Purpose | Reference |\n|---|---|---|---|\n| GET | `\u002Fauth\u002Fverify-mfa` | Verify an email MFA link (redirects) | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fauth\u002Fverify-mfa` | Submit an MFA code | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fcustom\u002Fmfa\u002F:reason` | Initiate a custom MFA flow | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| GET | `\u002Fauth\u002Fverify-custom-mfa` | Verify a custom MFA link | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fauth\u002Fverify-custom-mfa` | Submit a custom MFA code | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fupdate\u002Femail` | Change the user's email address | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#email-update-flow) |\n| POST | `\u002Fauth\u002Fforgot-password` | Initiate a password reset | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n| GET | `\u002Fauth\u002Freset-password` | Verify a password reset link | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n| POST | `\u002Fauth\u002Freset-password` | Submit a new password | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n\nCustom MFA and email update routes require a valid access token, refresh\ntoken, fingerprint, and a `checkForActiveMfa` pass before `protectRoute` runs\nfull [anomaly detection](\u002Fdocs\u002Fiam\u002Fessentials\u002Fanomalies) on these requests.\n\n### BFF Access Routes\n\nBackend-for-Frontend routes used by your application server to verify user sessions.\n\n| Method | Path | Purpose | Reference |\n|---|---|---|---|\n| GET | `\u002Fsecret\u002Fdata` | Verify the user's session and return authorization data | [BFF](\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff) |\n| GET | `\u002Fsecret\u002Faccesstoken\u002Fmetadata` | Return decoded access token payload and TTL | [BFF](\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff) |\n\nBoth routes require an access token (`Authorization: Bearer` header), a refresh\ntoken (session cookie), and a valid fingerprint. The routes also call\n`checkForActiveMfa` before `protectRoute` performs full JWT verification and\nanomaly detection.\n\n### API Token Management Routes\n\nHandles authenticated creation and management of API tokens.\n\n| Method | Path | Purpose | Reference |\n|---|---|---|---|\n| POST | `\u002Fapi\u002Fmanage\u002Fnew-token` | Create a new API token | [Creating Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fcreation) |\n| GET | `\u002Fapi\u002Fmanage\u002Flist-metadata` | List the user's valid API tokens | [Token Listing](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Flist) |\n| POST | `\u002Fapi\u002Fmanage\u002Frevoke` | Revoke an API token | [Revocation](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frevocation) |\n| POST | `\u002Fapi\u002Fmanage\u002Fmetadata` | Return metadata for one API token | [Metadata](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fmetadata) |\n| POST | `\u002Fapi\u002Fmanage\u002Frotate` | Rotate an API token | [Rotation](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Frotation) |\n| POST | `\u002Fapi\u002Fmanage\u002Fip-restriction-update` | Update a token IP whitelist | [IP Restriction](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fip-updates) |\n| POST | `\u002Fapi\u002Fmanage\u002Fprivilege-update` | Update a token privilege | [Privileges](\u002Fdocs\u002Fiam\u002Fessentials\u002Fapi\u002Fmanagement\u002Fprivilege) |\n\nAll API token management routes require `requireAccessToken`,\n`requireRefreshToken`, `getFingerPrint`, `checkForActiveMfa`, and\n`protectRoute`. The POST routes also enforce\n`contentType('application\u002Fjson')` and `express.json({ limit: '1kb' })`. The\n`GET \u002Fapi\u002Fmanage\u002Flist-metadata` branch skips the JSON guards because it does\nnot accept a request body.\n\n### Operational Config Endpoint\n\nA single endpoint mounted outside the route groups:\n\n| Method | Path | Purpose |\n|---|---|---|\n| GET | `\u002Foperational\u002Fconfig` | Return the cookie domain and access token TTL to a trusted client |\n\nThis endpoint is restricted by physical IP address. It compares `req.socket.remoteAddress` against `service.clientIp` or `service.proxy.ipToTrust`. Only the configured trusted client can access it.\n\n---\n\n## Terminal Handlers\n\nTwo handlers are registered after all routes:\n\n| Handler | Status | Body |\n|---|---|---|\n| `notFoundHandler` | `404` | `{ \"error\": \"The page you are looking for doesn't exists\" }` |\n| `finalUnHandledErrors` | `500` (or `res.statusCode` if > 415) | `{ \"error\": \"\u003Cmessage>\" }` |\n\nThe error handler logs the full error via pino before sending the response. It picks the status code from the current `res.statusCode` if it is above 415, otherwise defaults to 500.\n\n---\n\n## Configuration\n\nThe configuration object is validated at startup by a strict Zod schema. Every field is type-checked at runtime. If validation fails, the process exits with a detailed error showing which fields failed and why.\n\nThe schema covers database connections, JWT settings, password hashing, bot detection, rate limiting, magic links, OAuth providers, email credentials, and standalone server options. Each section is documented in its own essentials page, and the complete schema reference is available in the [Configuration reference](\u002Fdocs\u002Fiam\u002Fconfiguration).\n\n---\n\n## Docker Deployment\n\nThe standalone service is designed to run as a Docker container with encrypted configuration.\n\n### Container Architecture\n\nThe Dockerfile uses a multi-stage build:\n\n::steps{level=\"4\"}\n#### Go builder stage\nCompiles the [age](https:\u002F\u002Fgithub.com\u002FFiloSottile\u002Fage) encryption tool and [mmdbctl](https:\u002F\u002Fgithub.com\u002Fipinfo\u002Fmmdbctl) from source. These binaries are copied into later stages.\n\n#### Base stage\nStarts from `node:lts-slim`. Installs minimal runtime dependencies (`libatomic1`, `curl`, `ca-certificates`). Creates a non-root user (`appuser`, UID 10001) for the production stage.\n\n#### Builder stage\nInstalls npm dependencies with `npm ci`, runs `bot-detector init` to generate the [shield-base](\u002Fdocs\u002Fshield-base) data sources, and builds the TypeScript source with the configured build tool.\n\n#### Production stage\nCopies only the built artifacts, production `node_modules`, bot-detector data sources, and the `decrypt.sh` and `healthcheck.js` scripts. Drops all development tools, npm itself, and build caches. Runs as `appuser` (non-root) with `no-new-privileges` enforced.\n::\n\n\n### Entrypoint Flow\n\nThe container entrypoint is `decrypt.sh`, which runs before the main process:\n\n::steps{level=\"4\"}\n#### Check for existing config\nIf `\u002Frun\u002Fapp\u002Fconfig.json` already exists (from a previous run or manual mount), the script skips decryption and proceeds directly to the main command.\n\n#### Decrypt the configuration\nReads the age private key from `\u002Frun\u002Fsecrets\u002Fage_key` and the encrypted config from `\u002Frun\u002Fsecrets\u002Fencrypted_config`. Decrypts using `age -d` and writes the result to `\u002Frun\u002Fapp\u002Fconfig.json` with `0400` permissions (read-only for the owner).\n\n#### Launch the service\nCalls `exec \"$@\"`, which replaces the shell process with the Docker `CMD`: `node dist\u002Fservice.mjs`. The service `startServer` function then reads the config from the default path (`\u002Frun\u002Fapp\u002Fconfig.json`).\n::\n\n### Health Check\n\nDocker runs a health check every 30 seconds:\n\n```js [healthcheck.js]\nhttp.request({ host: '127.0.0.1', port: 10000, path: '\u002Fhealth', timeout: 2000 })\n```\n\nThe check hits `GET \u002Fhealth` on localhost. A `2xx` or `401` response means the service is healthy. The `401` case covers scenarios where HMAC is enabled but the health check runs without headers. Because the HMAC middleware skips localhost health checks, this typically returns `200`.\n\n---\n\n## Background Tasks\n\nAfter the server starts listening, `startServer` schedules three recurring background tasks:\n\n| Task | Command | Default Interval | Purpose |\n|---|---|---|---|\n| Bot detector refresh | `bot-detector refresh` | 24 hours | Updates IP reputation, geo, and threat intelligence data sources |\n| Bot detector generate | `bot-detector generate` | 3 days | Regenerates compiled MMDB databases from updated sources |\n| Disposable email list | `shield-base --email --path=dist` | 7 days | Updates the LMDB database of known disposable email domains |\n\nEach task runs with `nice -n 19` (lowest CPU priority) to minimize impact on request handling. Tasks are scheduled with `setTimeout` and reschedule themselves after completion, so the interval measures the gap between the end of one run and the start of the next.\n\n::tip\nThese tasks keep the bot detector's threat intelligence and the signup flow's disposable email list current without requiring a service restart. The first run is deferred by the full interval after startup, so a freshly deployed service uses the data that was baked into the image at build time.\n::\n\n---\n\n## Library Integration\n\nWhen using `@riavzon\u002Fauth` as a library in your own Express application, you skip `startServer` and either call `bootstrapApp` or mount the individual pieces yourself.\n\n### Using `bootstrapApp`\n\nThe simplest integration. Provides the full IAM service as a sub-application:\n\n```ts\nimport express from 'express'\nimport { bootstrapApp } from '@riavzon\u002Fauth\u002Fservice'\n\nconst mainApp = express()\n\nconst authApp = await bootstrapApp({\n  store: { main: { host: 'localhost', ... }, rate_limiters_pool: { store: { host: 'localhost', ... }, dbName: 'rate_limiters' } },\n  password: { pepper: 'your-pepper' },\n  botDetector: { enableBotDetector: false },\n  htmlSanitizer: {},\n  magic_links: { jwt_secret_key: '...', domain: 'https:\u002F\u002Fexample.com', notificationEmail: { ... } },\n  jwt: { jwt_secret_key: '...', access_tokens: {}, refresh_tokens: { refresh_ttl: 604800000, domain: 'example.com', MAX_SESSION_LIFE: 2592000000, maxAllowedSessionsPerUser: 5, byPassAnomaliesFor: 86400000 } },\n  email: { resend_key: '...', email: 'noreply@example.com' },\n})\n\nmainApp.use('\u002Fauth', authApp)\nmainApp.listen(3000)\n```\n\n::warning\nWhen mounting the auth app under a prefix like `\u002Fauth`, remember that the internal routes already include their own prefixes (e.g., `\u002Fauth\u002Fuser\u002Frefresh-session`). Adjust your mount point to avoid doubled path segments.\n::\n\n### Using Individual Routers\n\nFor more control, call `configuration()` to initialize the pools and config, then mount only the routers you need:\n\n```ts\nimport express from 'express'\nimport cookieParser from 'cookie-parser'\nimport {\n  configuration,\n  authenticationRoutes,\n  tokenRotationRoutes,\n  magicLinks,\n  bffAccessRoute,\n  apiVerificationRoute,\n  apiProtectedRoutes,\n} from '@riavzon\u002Fauth'\n\nconst app = express()\n\nawait configuration({\n  store: { ... },\n  password: { pepper: '...' },\n  \u002F\u002F ... rest of config\n})\n\n\u002F\u002F Mount only what you need\napp.use(apiVerificationRoute())\napp.use(cookieParser())\napp.use(authenticationRoutes)\napp.use(tokenRotationRoutes)\napp.use(magicLinks)\napp.use(bffAccessRoute)\napp.use(apiProtectedRoutes())\n\napp.listen(3000)\n```\n\n::warning\nYou must call `configuration()` before mounting any routers. The routers and\ncontrollers call `getConfiguration()` internally, which throws if the config\nhas not been initialized. Register `cookieParser()` before any router that\nreads `req.cookies`. The same applies to `getPool()`, `poolForLibrary()`, and\n`getDisposableEmailList()`.\n::\n\n### CLI Initialization\n\nThe package also exposes a CLI binary (`auth`) and an `initAuthData` function for one-time setup tasks like creating database tables and downloading the disposable email list:\n\n```ts\nimport { initAuthData } from '@riavzon\u002Fauth'\n\nawait initAuthData(config)\n```\n\nOr from the command line:\n\n```bash\nnpx auth .\u002Fconfig.json\n```\n\nThis runs `configuration()`, bot-detector setup, `makeTables()`, `createTables()`, and the disposable email LMDB download in sequence.\n\n---\n\n## Complete Route Map\n\nFor reference, here is every route the IAM service registers:\n\n| Method | Path | Auth Required | Body | Reference |\n|---|---|---|---|---|\n| GET | `\u002Fhealth` | No | None | |\n| POST | `\u002Fsignup` | canary cookie | JSON (1 KB) | [Signup](\u002Fdocs\u002Fiam\u002Fessentials\u002Fsignup) |\n| POST | `\u002Flogin` | canary cookie | JSON (1 KB) | [Login](\u002Fdocs\u002Fiam\u002Fessentials\u002Flogin) |\n| POST | `\u002Fauth\u002FOAuth\u002F:providerName` | canary cookie | JSON (4 KB) | [OAuth](\u002Fdocs\u002Fiam\u002Fessentials\u002Foauth) |\n| POST | `\u002Fauth\u002Fuser\u002Frefresh-session` | Refresh cookie | None (cookie only) | [Refresh Tokens](\u002Fdocs\u002Fiam\u002Fessentials\u002Frefresh-tokens) |\n| POST | `\u002Fauth\u002Flogout` | Access + Refresh | None (cookie only) | [Logout](\u002Fdocs\u002Fiam\u002Fessentials\u002Flogout) |\n| GET | `\u002Fauth\u002Fverify-mfa` | Magic link token | None | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fauth\u002Fverify-mfa` | Magic link token | JSON (1 KB) | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fcustom\u002Fmfa\u002F:reason` | Access + Refresh + JWT | JSON (1 KB) | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| GET | `\u002Fauth\u002Fverify-custom-mfa` | Access + Refresh + JWT | None | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fauth\u002Fverify-custom-mfa` | Access + Refresh + JWT | JSON (1 KB) | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa) |\n| POST | `\u002Fupdate\u002Femail` | Access + Refresh + JWT | JSON (1 KB) | [MFA](\u002Fdocs\u002Fiam\u002Fessentials\u002Fmfa#email-update-flow) |\n| POST | `\u002Fauth\u002Fforgot-password` | None | JSON (1 KB) | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n| GET | `\u002Fauth\u002Freset-password` | Magic link token | None | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n| POST | `\u002Fauth\u002Freset-password` | Magic link token | JSON (1 KB) | [Password Reset](\u002Fdocs\u002Fiam\u002Fessentials\u002Fpassword-reset) |\n| GET | `\u002Fsecret\u002Fdata` | Access + Refresh + JWT | None | [BFF](\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff) |\n| GET | `\u002Fsecret\u002Faccesstoken\u002Fmetadata` | Access + Refresh + JWT | None | [BFF](\u002Fdocs\u002Fiam\u002Fessentials\u002Fbff) |\n| GET | `\u002Foperational\u002Fconfig` | Trusted IP only | None | |\n\n---\n\n## Summary\n\n| Concept | Description |\n|---|---|\n| **Two entry points** | `@riavzon\u002Fauth` for library usage, `@riavzon\u002Fauth\u002Fservice` for standalone deployment |\n| **`startServer`** | Reads encrypted config, calls `bootstrapApp`, deletes config from disk, listens on port |\n| **`bootstrapApp`** | Initializes pools, tables, middleware, routes. Returns Express app |\n| **Config lifecycle** | Decrypted at container start, parsed at boot, deleted from disk, frozen in memory |\n| **Middleware order** | Logger, helmet, cache headers, IP validation, HMAC (optional), JSON parser, cookie parser, bot detector |\n| **Route groups** | Authentication, token rotation, magic links, BFF access, operational config |\n| **Background tasks** | Bot detector data refresh (daily), database generation (3 days), disposable email update (weekly) |\n| **Docker security** | Read-only filesystem, no capabilities, non-root user, tmpfs for secrets, PID limits |\n| **Health check** | `GET \u002Fhealth` returns `200 OK`, polled every 30 seconds by Docker |\n",{"title":159,"description":4437},"B7z58MJigooL6B23G-dT-BRslN51TeLXgZjq0CEMcKc",[4445,4446],{"title":155,"path":156,"stem":157,"children":-1},{"title":163,"path":164,"stem":165,"children":-1},{"id":851,"title":159,"body":4448,"description":4437,"extension":4438,"icon":15,"meta":7214,"module":4440,"navigation":8,"path":160,"rawbody":4441,"seo":7215,"stem":161,"__hash__":4443},{"type":853,"value":4449,"toc":7180},[4450,4459,4463,4465,4467,4469,4513,4527,4531,4533,4537,4541,4543,4593,4635,4641,4643,4647,4653,4717,4719,4721,4875,4877,4879,4881,5072,5078,5080,5082,5084,5086,5088,5118,5128,5130,5132,5210,5226,5228,5230,5294,5300,5302,5304,5446,5454,5456,5458,5502,5510,5512,5514,5628,5646,5648,5650,5674,5682,5684,5686,5688,5732,5736,5738,5740,5742,5746,5748,5750,5752,5754,5756,5806,5808,5812,5848,5850,5852,5906,5918,5920,5922,5926,5978,5984,5988,5990,5992,6000,6004,6006,6384,6392,6394,6398,6682,6700,6702,6708,6746,6748,6760,6768,6770,6772,6774,7076,7078,7080,7178],[856,4451,858,4452,863,4454,871,4457,875],{},[860,4453,862],{},[865,4455,870],{"href":867,"rel":4456},[869],[860,4458,874],{},[856,4460,878,4461,882],{},[860,4462,881],{},[884,4464],{},[887,4466,890],{"id":889},[856,4468,893],{},[895,4470,4471,4481],{},[898,4472,4473],{},[901,4474,4475,4477,4479],{},[904,4476,906],{},[904,4478,909],{},[904,4480,912],{},[914,4482,4483,4495],{},[901,4484,4485,4489,4493],{},[919,4486,4487],{},[860,4488,874],{},[919,4490,4491],{},[860,4492,927],{},[919,4494,930],{},[901,4496,4497,4501,4505],{},[919,4498,4499],{},[860,4500,862],{},[919,4502,4503],{},[860,4504,941],{},[919,4506,944,4507,947,4509,951,4511,955],{},[860,4508,881],{},[860,4510,950],{},[860,4512,954],{},[856,4514,958,4515,961,4517,964,4519,968,4521,972,4523,976,4525,980],{},[860,4516,874],{},[860,4518,862],{},[860,4520,967],{},[860,4522,971],{},[860,4524,975],{},[860,4526,979],{},[856,4528,983,4529,955],{},[865,4530,986],{"href":247},[884,4532],{},[887,4534,4535],{"id":991},[860,4536,950],{},[856,4538,996,4539,999],{},[860,4540,950],{},[1001,4542,1004],{"id":1003},[1006,4544,4545,4547,4555,4557,4565,4567,4571,4573,4581,4583],{"level":1008},[1010,4546,1013],{"id":1012},[856,4548,1016,4549,1020,4551,1024,4553,1028],{},[860,4550,1019],{},[860,4552,1023],{},[860,4554,1027],{},[1010,4556,1032],{"id":1031},[856,4558,1035,4559,1039,4561,1042,4563,1046],{},[860,4560,1038],{},[860,4562,881],{},[865,4564,1045],{"href":238},[1010,4566,1050],{"id":1049},[856,4568,1053,4569,1057],{},[860,4570,1056],{},[1010,4572,1061],{"id":1060},[856,4574,1064,4575,1068,4577,1072,4579,1076],{},[860,4576,1067],{},[860,4578,1071],{},[860,4580,1075],{},[1010,4582,1080],{"id":1079},[856,4584,1083,4585,1087,4587,1091,4589,1087,4591,1098],{},[860,4586,1086],{},[860,4588,1090],{},[860,4590,1094],{},[860,4592,1097],{},[1100,4594,4595],{"className":1102,"code":1103,"language":1104,"meta":1105,"style":1105},[860,4596,4597,4615,4619,4623,4627],{"__ignoreMap":1105},[1109,4598,4599,4601,4603,4605,4607,4609,4611,4613],{"class":1111,"line":1112},[1109,4600,1116],{"class":1115},[1109,4602,1120],{"class":1119},[1109,4604,950],{"class":1123},[1109,4606,1126],{"class":1119},[1109,4608,1129],{"class":1115},[1109,4610,1133],{"class":1132},[1109,4612,862],{"class":1136},[1109,4614,1139],{"class":1132},[1109,4616,4617],{"class":1111,"line":1142},[1109,4618,1145],{"emptyLinePlaceholder":8},[1109,4620,4621],{"class":1111,"line":1148},[1109,4622,1152],{"class":1151},[1109,4624,4625],{"class":1111,"line":1155},[1109,4626,1158],{"class":1151},[1109,4628,4629,4631,4633],{"class":1111,"line":1161},[1109,4630,1164],{"class":1115},[1109,4632,1168],{"class":1167},[1109,4634,1171],{"class":1119},[1173,4636,4637],{},[856,4638,1177,4639,1181],{},[860,4640,1180],{},[884,4642],{},[887,4644,4645],{"id":1186},[860,4646,881],{},[856,4648,1191,4649,1195,4651,1199],{},[860,4650,1194],{},[860,4652,1198],{},[1100,4654,4655],{"className":1102,"code":1202,"language":1104,"meta":1105,"style":1105},[860,4656,4657,4675,4695,4699],{"__ignoreMap":1105},[1109,4658,4659,4661,4663,4665,4667,4669,4671,4673],{"class":1111,"line":1112},[1109,4660,1116],{"class":1115},[1109,4662,1120],{"class":1119},[1109,4664,881],{"class":1123},[1109,4666,1126],{"class":1119},[1109,4668,1129],{"class":1115},[1109,4670,1133],{"class":1132},[1109,4672,862],{"class":1136},[1109,4674,1139],{"class":1132},[1109,4676,4677,4679,4681,4683,4685,4687,4689,4691,4693],{"class":1111,"line":1142},[1109,4678,1116],{"class":1115},[1109,4680,1229],{"class":1115},[1109,4682,1120],{"class":1119},[1109,4684,1194],{"class":1123},[1109,4686,1126],{"class":1119},[1109,4688,1129],{"class":1115},[1109,4690,1133],{"class":1132},[1109,4692,874],{"class":1136},[1109,4694,1139],{"class":1132},[1109,4696,4697],{"class":1111,"line":1148},[1109,4698,1145],{"emptyLinePlaceholder":8},[1109,4700,4701,4703,4705,4707,4709,4711,4713,4715],{"class":1111,"line":1155},[1109,4702,1253],{"class":1252},[1109,4704,1257],{"class":1256},[1109,4706,1261],{"class":1260},[1109,4708,1264],{"class":1115},[1109,4710,1267],{"class":1167},[1109,4712,1270],{"class":1119},[1109,4714,1273],{"class":1123},[1109,4716,1276],{"class":1119},[1001,4718,1280],{"id":1279},[856,4720,1283],{},[1006,4722,4723,4725,4741,4743,4747,4774,4780,4782,4799,4801,4809,4811,4825,4827,4849,4851,4853,4855,4857,4859,4863,4865],{"level":1008},[1010,4724,1289],{"id":1288},[856,4726,1292,4727,1296,4729,1300,4731,1304,4733,1308,4735,1312,4737,1316,4739,1319],{},[860,4728,1295],{},[860,4730,1299],{},[860,4732,1303],{},[860,4734,1307],{},[860,4736,1311],{},[860,4738,1315],{},[865,4740,111],{"href":112},[1010,4742,1323],{"id":1322},[856,4744,1053,4745,1329],{},[860,4746,1328],{},[1331,4748,4749,4757,4767],{},[1334,4750,4751,1340,4753,1344,4755,1348],{},[1337,4752,1339],{},[860,4754,1343],{},[860,4756,1347],{},[1334,4758,4759,1354,4761,1344,4763,1361,4765,1365],{},[1337,4760,1353],{},[860,4762,1357],{},[860,4764,1360],{},[865,4766,1364],{"href":148},[1334,4768,4769,1371,4771,1377],{},[1337,4770,1370],{},[865,4772,1376],{"href":1374,"rel":4773},[869],[856,4775,1380,4776,1384,4778,1388],{},[860,4777,1383],{},[860,4779,1387],{},[1010,4781,1392],{"id":1391},[856,4783,1395,4784,1401,4787,972,4789,1408,4791,1308,4793,1414,4795,1418,4797,1422],{},[865,4785,1400],{"href":1398,"rel":4786},[869],[860,4788,1404],{},[860,4790,1407],{},[860,4792,1411],{},[860,4794,1094],{},[860,4796,1417],{},[860,4798,1421],{},[1010,4800,1426],{"id":1425},[856,4802,1429,4803,1433,4805,1437,4807,1441],{},[860,4804,1432],{},[860,4806,1436],{},[860,4808,1440],{},[1010,4810,1445],{"id":1444},[856,4812,1448,4813,1454,4817,1458,4819,1462,4821,1466,4823,1470],{},[865,4814,4815],{"href":35},[860,4816,1453],{},[860,4818,1457],{},[860,4820,1461],{},[860,4822,1465],{},[860,4824,1469],{},[1010,4826,1474],{"id":1473},[856,4828,1477,4829,1481,4831,1485,4833,1489,4835,1493,4837,1497,4839,1501,4841,1505,4843,1489,4845,1511,4847,1046],{},[860,4830,1480],{},[860,4832,1484],{},[860,4834,1488],{},[860,4836,1492],{},[860,4838,1496],{},[860,4840,1500],{},[860,4842,1504],{},[860,4844,1492],{},[860,4846,1510],{},[865,4848,151],{"href":152},[1010,4850,1517],{"id":1516},[856,4852,1520],{},[1010,4854,1524],{"id":1523},[856,4856,1527],{},[1010,4858,1531],{"id":1530},[856,4860,1053,4861,1537],{},[860,4862,1536],{},[1010,4864,1541],{"id":1540},[856,4866,1544,4867,1548,4869,1552,4871,1556,4873,1560],{},[860,4868,1547],{},[860,4870,1551],{},[860,4872,1555],{},[860,4874,1559],{},[884,4876],{},[887,4878,1566],{"id":1565},[856,4880,1569],{},[895,4882,4883,4895],{},[898,4884,4885],{},[901,4886,4887,4889,4891,4893],{},[904,4888,1578],{},[904,4890,1581],{},[904,4892,912],{},[904,4894,1586],{},[914,4896,4897,4918,4932,4952,4970,4988,5012,5028,5040,5058],{},[901,4898,4899,4901,4905,4914],{},[919,4900,1593],{},[919,4902,4903],{},[860,4904,1598],{},[919,4906,1601,4907,1607,4910,1611,4912,1615],{},[865,4908,1606],{"href":1604,"rel":4909},[869],[860,4911,1610],{},[860,4913,1614],{},[919,4915,4916],{},[865,4917,143],{"href":144},[901,4919,4920,4922,4926,4930],{},[919,4921,1624],{},[919,4923,4924,1630],{},[860,4925,1629],{},[919,4927,1633,4928,1637],{},[860,4929,1636],{},[919,4931],{},[901,4933,4934,4936,4940,4950],{},[919,4935,1644],{},[919,4937,4938],{},[860,4939,1649],{},[919,4941,1652,4942,1489,4944,1489,4946,1489,4948,955],{},[860,4943,1655],{},[860,4945,1658],{},[860,4947,1661],{},[860,4949,1664],{},[919,4951],{},[901,4953,4954,4956,4960,4968],{},[919,4955,1008],{},[919,4957,4958],{},[860,4959,1675],{},[919,4961,1678,4962,1489,4964,1489,4966,1688],{},[860,4963,1681],{},[860,4965,1684],{},[860,4967,1687],{},[919,4969],{},[901,4971,4972,4974,4978,4986],{},[919,4973,1695],{},[919,4975,4976],{},[860,4977,1700],{},[919,4979,1703,4980,1706,4982,1710,4984,1714],{},[860,4981,1421],{},[860,4983,1709],{},[860,4985,1713],{},[919,4987],{},[901,4989,4990,4992,4996,5008],{},[919,4991,1721],{},[919,4993,4994,1727],{},[860,4995,1726],{},[919,4997,1730,4998,1734,5000,1489,5002,1489,5004,1497,5006,1747],{},[860,4999,1733],{},[860,5001,1737],{},[860,5003,1740],{},[860,5005,1743],{},[860,5007,1746],{},[919,5009,5010],{},[865,5011,1752],{"href":136},[901,5013,5014,5016,5020,5024],{},[919,5015,1757],{},[919,5017,5018],{},[860,5019,1762],{},[919,5021,1429,5022,1768],{},[860,5023,1767],{},[919,5025,5026],{},[865,5027,167],{"href":178},[901,5029,5030,5032,5036,5038],{},[919,5031,1777],{},[919,5033,5034],{},[860,5035,1782],{},[919,5037,1785],{},[919,5039],{},[901,5041,5042,5044,5048,5054],{},[919,5043,1792],{},[919,5045,5046],{},[860,5047,1797],{},[919,5049,1800,5050,1804,5052,955],{},[860,5051,1803],{},[860,5053,1807],{},[919,5055,5056],{},[865,5057,155],{"href":156},[901,5059,5060,5062,5066,5068],{},[919,5061,1816],{},[919,5063,5064],{},[860,5065,1469],{},[919,5067,1823],{},[919,5069,5070],{},[865,5071,33],{"href":35},[1173,5073,5074],{},[856,5075,1832,5076,1835],{},[860,5077,1469],{},[884,5079],{},[887,5081,1841],{"id":1840},[856,5083,1844],{},[1001,5085,1848],{"id":1847},[856,5087,1851],{},[895,5089,5090,5102],{},[898,5091,5092],{},[901,5093,5094,5096,5098,5100],{},[904,5095,1860],{},[904,5097,1863],{},[904,5099,912],{},[904,5101,1586],{},[914,5103,5104],{},[901,5105,5106,5108,5112,5114],{},[919,5107,1874],{},[919,5109,5110],{},[860,5111,1879],{},[919,5113,1882],{},[919,5115,5116],{},[865,5117,167],{"href":178},[856,5119,1889,5120,1489,5122,1894,5124,1898,5126,1902],{},[860,5121,1782],{},[860,5123,1797],{},[860,5125,1897],{},[860,5127,1901],{},[1001,5129,1906],{"id":1905},[856,5131,1909],{},[895,5133,5134,5148],{},[898,5135,5136],{},[901,5137,5138,5140,5142,5144,5146],{},[904,5139,1860],{},[904,5141,1863],{},[904,5143,912],{},[904,5145,1581],{},[904,5147,1586],{},[914,5149,5150,5170,5190],{},[901,5151,5152,5154,5158,5160,5166],{},[919,5153,1932],{},[919,5155,5156],{},[860,5157,1937],{},[919,5159,1940],{},[919,5161,5162,1489,5164,1949],{},[860,5163,1945],{},[860,5165,1948],{},[919,5167,5168],{},[865,5169,99],{"href":100},[901,5171,5172,5174,5178,5180,5186],{},[919,5173,1932],{},[919,5175,5176],{},[860,5177,1962],{},[919,5179,1965],{},[919,5181,5182,1489,5184,1949],{},[860,5183,1945],{},[860,5185,1948],{},[919,5187,5188],{},[865,5189,103],{"href":104},[901,5191,5192,5194,5198,5200,5206],{},[919,5193,1932],{},[919,5195,5196],{},[860,5197,1984],{},[919,5199,1987],{},[919,5201,5202,1489,5204,1949],{},[860,5203,1945],{},[860,5205,1994],{},[919,5207,5208],{},[865,5209,111],{"href":112},[856,5211,2001,5212,2005,5214,2009,5216,2013,5218,2017,5220,2021,5222,2025,5224,2029],{},[860,5213,2004],{},[860,5215,2008],{},[860,5217,2012],{},[860,5219,2016],{},[860,5221,2020],{},[865,5223,2024],{"href":35},[860,5225,2028],{},[1001,5227,2033],{"id":2032},[856,5229,2036],{},[895,5231,5232,5246],{},[898,5233,5234],{},[901,5235,5236,5238,5240,5242,5244],{},[904,5237,1860],{},[904,5239,1863],{},[904,5241,912],{},[904,5243,1581],{},[904,5245,1586],{},[914,5247,5248,5272],{},[901,5249,5250,5252,5256,5258,5268],{},[919,5251,1932],{},[919,5253,5254],{},[860,5255,2063],{},[919,5257,2066],{},[919,5259,5260,1489,5262,1489,5264,1489,5266],{},[860,5261,2071],{},[860,5263,2074],{},[860,5265,2077],{},[860,5267,2080],{},[919,5269,5270],{},[865,5271,91],{"href":92},[901,5273,5274,5276,5280,5282,5290],{},[919,5275,1932],{},[919,5277,5278],{},[860,5279,2093],{},[919,5281,2096],{},[919,5283,5284,1489,5286,1489,5288],{},[860,5285,2071],{},[860,5287,2103],{},[860,5289,2074],{},[919,5291,5292],{},[865,5293,107],{"href":108},[856,5295,996,5296,2114,5298,2118],{},[860,5297,2074],{},[860,5299,2117],{},[1001,5301,2122],{"id":2121},[856,5303,2125],{},[895,5305,5306,5318],{},[898,5307,5308],{},[901,5309,5310,5312,5314,5316],{},[904,5311,1860],{},[904,5313,1863],{},[904,5315,912],{},[904,5317,1586],{},[914,5319,5320,5334,5348,5362,5376,5390,5404,5418,5432],{},[901,5321,5322,5324,5328,5330],{},[919,5323,1874],{},[919,5325,5326],{},[860,5327,2150],{},[919,5329,2153],{},[919,5331,5332],{},[865,5333,123],{"href":124},[901,5335,5336,5338,5342,5344],{},[919,5337,1932],{},[919,5339,5340],{},[860,5341,2150],{},[919,5343,2168],{},[919,5345,5346],{},[865,5347,123],{"href":124},[901,5349,5350,5352,5356,5358],{},[919,5351,1932],{},[919,5353,5354],{},[860,5355,2181],{},[919,5357,2184],{},[919,5359,5360],{},[865,5361,123],{"href":124},[901,5363,5364,5366,5370,5372],{},[919,5365,1874],{},[919,5367,5368],{},[860,5369,2197],{},[919,5371,2200],{},[919,5373,5374],{},[865,5375,123],{"href":124},[901,5377,5378,5380,5384,5386],{},[919,5379,1932],{},[919,5381,5382],{},[860,5383,2197],{},[919,5385,2215],{},[919,5387,5388],{},[865,5389,123],{"href":124},[901,5391,5392,5394,5398,5400],{},[919,5393,1932],{},[919,5395,5396],{},[860,5397,2228],{},[919,5399,2231],{},[919,5401,5402],{},[865,5403,123],{"href":2236},[901,5405,5406,5408,5412,5414],{},[919,5407,1932],{},[919,5409,5410],{},[860,5411,2245],{},[919,5413,2248],{},[919,5415,5416],{},[865,5417,163],{"href":164},[901,5419,5420,5422,5426,5428],{},[919,5421,1874],{},[919,5423,5424],{},[860,5425,2261],{},[919,5427,2264],{},[919,5429,5430],{},[865,5431,163],{"href":164},[901,5433,5434,5436,5440,5442],{},[919,5435,1932],{},[919,5437,5438],{},[860,5439,2261],{},[919,5441,2279],{},[919,5443,5444],{},[865,5445,163],{"href":164},[856,5447,2286,5448,2289,5450,2293,5452,2297],{},[860,5449,2080],{},[860,5451,2292],{},[865,5453,2296],{"href":96},[1001,5455,2301],{"id":2300},[856,5457,2304],{},[895,5459,5460,5472],{},[898,5461,5462],{},[901,5463,5464,5466,5468,5470],{},[904,5465,1860],{},[904,5467,1863],{},[904,5469,912],{},[904,5471,1586],{},[914,5473,5474,5488],{},[901,5475,5476,5478,5482,5484],{},[919,5477,1874],{},[919,5479,5480],{},[860,5481,2329],{},[919,5483,2332],{},[919,5485,5486],{},[865,5487,2337],{"href":132},[901,5489,5490,5492,5496,5498],{},[919,5491,1874],{},[919,5493,5494],{},[860,5495,2346],{},[919,5497,2349],{},[919,5499,5500],{},[865,5501,2337],{"href":132},[856,5503,2356,5504,2360,5506,2363,5508,2366],{},[860,5505,2359],{},[860,5507,2080],{},[860,5509,2292],{},[1001,5511,2370],{"id":2369},[856,5513,2373],{},[895,5515,5516,5528],{},[898,5517,5518],{},[901,5519,5520,5522,5524,5526],{},[904,5521,1860],{},[904,5523,1863],{},[904,5525,912],{},[904,5527,1586],{},[914,5529,5530,5544,5558,5572,5586,5600,5614],{},[901,5531,5532,5534,5538,5540],{},[919,5533,1932],{},[919,5535,5536],{},[860,5537,2398],{},[919,5539,2401],{},[919,5541,5542],{},[865,5543,173],{"href":174},[901,5545,5546,5548,5552,5554],{},[919,5547,1874],{},[919,5549,5550],{},[860,5551,2414],{},[919,5553,2417],{},[919,5555,5556],{},[865,5557,207],{"href":208},[901,5559,5560,5562,5566,5568],{},[919,5561,1932],{},[919,5563,5564],{},[860,5565,2430],{},[919,5567,2433],{},[919,5569,5570],{},[865,5571,191],{"href":192},[901,5573,5574,5576,5580,5582],{},[919,5575,1932],{},[919,5577,5578],{},[860,5579,2446],{},[919,5581,2449],{},[919,5583,5584],{},[865,5585,203],{"href":204},[901,5587,5588,5590,5594,5596],{},[919,5589,1932],{},[919,5591,5592],{},[860,5593,2462],{},[919,5595,2465],{},[919,5597,5598],{},[865,5599,195],{"href":196},[901,5601,5602,5604,5608,5610],{},[919,5603,1932],{},[919,5605,5606],{},[860,5607,2478],{},[919,5609,2481],{},[919,5611,5612],{},[865,5613,199],{"href":200},[901,5615,5616,5618,5622,5624],{},[919,5617,1932],{},[919,5619,5620],{},[860,5621,2494],{},[919,5623,2497],{},[919,5625,5626],{},[865,5627,187],{"href":188},[856,5629,2504,5630,1493,5632,1489,5634,1489,5636,2513,5638,2516,5640,947,5642,2522,5644,2526],{},[860,5631,2103],{},[860,5633,2071],{},[860,5635,2077],{},[860,5637,2080],{},[860,5639,2292],{},[860,5641,1945],{},[860,5643,2521],{},[860,5645,2525],{},[1001,5647,2530],{"id":2529},[856,5649,2533],{},[895,5651,5652,5662],{},[898,5653,5654],{},[901,5655,5656,5658,5660],{},[904,5657,1860],{},[904,5659,1863],{},[904,5661,912],{},[914,5663,5664],{},[901,5665,5666,5668,5672],{},[919,5667,1874],{},[919,5669,5670],{},[860,5671,2556],{},[919,5673,2559],{},[856,5675,2562,5676,2566,5678,1308,5680,2572],{},[860,5677,2565],{},[860,5679,2569],{},[860,5681,1417],{},[884,5683],{},[887,5685,2578],{"id":2577},[856,5687,2581],{},[895,5689,5690,5700],{},[898,5691,5692],{},[901,5693,5694,5696,5698],{},[904,5695,2590],{},[904,5697,2593],{},[904,5699,2596],{},[914,5701,5702,5716],{},[901,5703,5704,5708,5712],{},[919,5705,5706],{},[860,5707,1547],{},[919,5709,5710],{},[860,5711,1551],{},[919,5713,5714],{},[860,5715,2613],{},[901,5717,5718,5722,5728],{},[919,5719,5720],{},[860,5721,1555],{},[919,5723,5724,2624,5726,2628],{},[860,5725,1559],{},[860,5727,2627],{},[919,5729,5730],{},[860,5731,2633],{},[856,5733,2636,5734,2639],{},[860,5735,2627],{},[884,5737],{},[887,5739,237],{"id":2644},[856,5741,2647],{},[856,5743,2650,5744,955],{},[865,5745,1045],{"href":238},[884,5747],{},[887,5749,2658],{"id":2657},[856,5751,2661],{},[1001,5753,2665],{"id":2664},[856,5755,2668],{},[1006,5757,5758,5760,5768,5770,5782,5784,5792,5794],{"level":1008},[1010,5759,2674],{"id":2673},[856,5761,2677,5762,2681,5765,2687],{},[865,5763,870],{"href":867,"rel":5764},[869],[865,5766,2686],{"href":2684,"rel":5767},[869],[1010,5769,2691],{"id":2690},[856,5771,2694,5772,2698,5774,1489,5776,1489,5778,2708,5780,2712],{},[860,5773,2697],{},[860,5775,2701],{},[860,5777,2704],{},[860,5779,2707],{},[860,5781,2711],{},[1010,5783,2716],{"id":2715},[856,5785,2719,5786,2723,5788,2727,5790,2731],{},[860,5787,2722],{},[860,5789,2726],{},[865,5791,2730],{"href":42},[1010,5793,2735],{"id":2734},[856,5795,2738,5796,2742,5798,947,5800,2747,5802,2750,5804,2754],{},[860,5797,2741],{},[860,5799,1180],{},[860,5801,1440],{},[860,5803,2711],{},[860,5805,2753],{},[1001,5807,2758],{"id":2757},[856,5809,2761,5810,2764],{},[860,5811,1180],{},[1006,5813,5814,5816,5820,5822,5834,5836],{"level":1008},[1010,5815,2770],{"id":2769},[856,5817,2773,5818,2776],{},[860,5819,1023],{},[1010,5821,2780],{"id":2779},[856,5823,2783,5824,2787,5826,2791,5828,2795,5830,2798,5832,2802],{},[860,5825,2786],{},[860,5827,2790],{},[860,5829,2794],{},[860,5831,1023],{},[860,5833,2801],{},[1010,5835,2806],{"id":2805},[856,5837,1053,5838,2812,5840,2815,5842,2819,5844,2822,5846,2825],{},[860,5839,2811],{},[860,5841,979],{},[860,5843,2818],{},[860,5845,950],{},[860,5847,1023],{},[1001,5849,2829],{"id":2828},[856,5851,2832],{},[1100,5853,5854],{"className":2835,"code":2836,"filename":1440,"language":2837,"meta":1105,"style":1105},[860,5855,5856],{"__ignoreMap":1105},[1109,5857,5858,5860,5862,5864,5866,5868,5870,5872,5874,5876,5878,5880,5882,5884,5886,5888,5890,5892,5894,5896,5898,5900,5902,5904],{"class":1111,"line":1112},[1109,5859,2844],{"class":1123},[1109,5861,955],{"class":1119},[1109,5863,2849],{"class":1167},[1109,5865,2852],{"class":1119},[1109,5867,2855],{"class":1123},[1109,5869,2859],{"class":2858},[1109,5871,1133],{"class":1132},[1109,5873,2864],{"class":1136},[1109,5875,2867],{"class":1132},[1109,5877,1489],{"class":1119},[1109,5879,2872],{"class":1123},[1109,5881,2859],{"class":2858},[1109,5883,2878],{"class":2877},[1109,5885,1489],{"class":1119},[1109,5887,2883],{"class":1123},[1109,5889,2859],{"class":2858},[1109,5891,1133],{"class":1132},[1109,5893,2890],{"class":1136},[1109,5895,2867],{"class":1132},[1109,5897,1489],{"class":1119},[1109,5899,2897],{"class":1123},[1109,5901,2859],{"class":2858},[1109,5903,2902],{"class":2877},[1109,5905,2905],{"class":1119},[856,5907,2908,5908,2911,5910,1308,5912,2918,5914,2921,5916,955],{},[860,5909,1432],{},[860,5911,2914],{},[860,5913,2917],{},[860,5915,2917],{},[860,5917,2924],{},[884,5919],{},[887,5921,2930],{"id":2929},[856,5923,2933,5924,2936],{},[860,5925,950],{},[895,5927,5928,5940],{},[898,5929,5930],{},[901,5931,5932,5934,5936,5938],{},[904,5933,2945],{},[904,5935,2948],{},[904,5937,2951],{},[904,5939,912],{},[914,5941,5942,5954,5966],{},[901,5943,5944,5946,5950,5952],{},[919,5945,2960],{},[919,5947,5948],{},[860,5949,2965],{},[919,5951,2968],{},[919,5953,2971],{},[901,5955,5956,5958,5962,5964],{},[919,5957,2976],{},[919,5959,5960],{},[860,5961,2981],{},[919,5963,2984],{},[919,5965,2987],{},[901,5967,5968,5970,5974,5976],{},[919,5969,2992],{},[919,5971,5972],{},[860,5973,2997],{},[919,5975,3000],{},[919,5977,3003],{},[856,5979,3006,5980,3010,5982,3014],{},[860,5981,3009],{},[860,5983,3013],{},[3016,5985,5986],{},[856,5987,3020],{},[884,5989],{},[887,5991,3026],{"id":3025},[856,5993,3029,5994,3032,5996,3035,5998,3038],{},[860,5995,874],{},[860,5997,950],{},[860,5999,881],{},[1001,6001,3042,6002],{"id":3041},[860,6003,881],{},[856,6005,3047],{},[1100,6007,6008],{"className":1102,"code":3050,"language":1104,"meta":1105,"style":1105},[860,6009,6010,6024,6042,6046,6058,6062,6076,6146,6166,6182,6190,6232,6308,6340,6344,6348,6370],{"__ignoreMap":1105},[1109,6011,6012,6014,6016,6018,6020,6022],{"class":1111,"line":1112},[1109,6013,1116],{"class":1115},[1109,6015,3059],{"class":1123},[1109,6017,3062],{"class":1115},[1109,6019,1133],{"class":1132},[1109,6021,3067],{"class":1136},[1109,6023,1139],{"class":1132},[1109,6025,6026,6028,6030,6032,6034,6036,6038,6040],{"class":1111,"line":1142},[1109,6027,1116],{"class":1115},[1109,6029,1120],{"class":1119},[1109,6031,881],{"class":1123},[1109,6033,1126],{"class":1119},[1109,6035,1129],{"class":1115},[1109,6037,1133],{"class":1132},[1109,6039,862],{"class":1136},[1109,6041,1139],{"class":1132},[1109,6043,6044],{"class":1111,"line":1148},[1109,6045,1145],{"emptyLinePlaceholder":8},[1109,6047,6048,6050,6052,6054,6056],{"class":1111,"line":1155},[1109,6049,1253],{"class":1252},[1109,6051,3098],{"class":1256},[1109,6053,1261],{"class":1260},[1109,6055,3059],{"class":1167},[1109,6057,1171],{"class":1119},[1109,6059,6060],{"class":1111,"line":1161},[1109,6061,1145],{"emptyLinePlaceholder":8},[1109,6063,6064,6066,6068,6070,6072,6074],{"class":1111,"line":3111},[1109,6065,1253],{"class":1252},[1109,6067,3116],{"class":1256},[1109,6069,1261],{"class":1260},[1109,6071,1264],{"class":1115},[1109,6073,1267],{"class":1167},[1109,6075,3125],{"class":1119},[1109,6077,6078,6080,6082,6084,6086,6088,6090,6092,6094,6096,6098,6100,6102,6104,6106,6108,6110,6112,6114,6116,6118,6120,6122,6124,6126,6128,6130,6132,6134,6136,6138,6140,6142,6144],{"class":1111,"line":3128},[1109,6079,3131],{"class":1123},[1109,6081,2859],{"class":2858},[1109,6083,1120],{"class":1119},[1109,6085,3138],{"class":1123},[1109,6087,2859],{"class":2858},[1109,6089,1120],{"class":1119},[1109,6091,2855],{"class":1123},[1109,6093,2859],{"class":2858},[1109,6095,1133],{"class":1132},[1109,6097,3151],{"class":1136},[1109,6099,2867],{"class":1132},[1109,6101,1489],{"class":1119},[1109,6103,3158],{"class":1260},[1109,6105,3161],{"class":1119},[1109,6107,3164],{"class":1123},[1109,6109,2859],{"class":2858},[1109,6111,1120],{"class":1119},[1109,6113,3171],{"class":1123},[1109,6115,2859],{"class":2858},[1109,6117,1120],{"class":1119},[1109,6119,2855],{"class":1123},[1109,6121,2859],{"class":2858},[1109,6123,1133],{"class":1132},[1109,6125,3151],{"class":1136},[1109,6127,2867],{"class":1132},[1109,6129,1489],{"class":1119},[1109,6131,3158],{"class":1260},[1109,6133,3161],{"class":1119},[1109,6135,3194],{"class":1123},[1109,6137,2859],{"class":2858},[1109,6139,1133],{"class":1132},[1109,6141,3201],{"class":1136},[1109,6143,2867],{"class":1132},[1109,6145,3206],{"class":1119},[1109,6147,6148,6150,6152,6154,6156,6158,6160,6162,6164],{"class":1111,"line":3209},[1109,6149,3212],{"class":1123},[1109,6151,2859],{"class":2858},[1109,6153,1120],{"class":1119},[1109,6155,3219],{"class":1123},[1109,6157,2859],{"class":2858},[1109,6159,1133],{"class":1132},[1109,6161,3226],{"class":1136},[1109,6163,2867],{"class":1132},[1109,6165,3231],{"class":1119},[1109,6167,6168,6170,6172,6174,6176,6178,6180],{"class":1111,"line":3234},[1109,6169,3237],{"class":1123},[1109,6171,2859],{"class":2858},[1109,6173,1120],{"class":1119},[1109,6175,3244],{"class":1123},[1109,6177,2859],{"class":2858},[1109,6179,3250],{"class":3249},[1109,6181,3231],{"class":1119},[1109,6183,6184,6186,6188],{"class":1111,"line":3255},[1109,6185,3258],{"class":1123},[1109,6187,2859],{"class":2858},[1109,6189,3263],{"class":1119},[1109,6191,6192,6194,6196,6198,6200,6202,6204,6206,6208,6210,6212,6214,6216,6218,6220,6222,6224,6226,6228,6230],{"class":1111,"line":3266},[1109,6193,3269],{"class":1123},[1109,6195,2859],{"class":2858},[1109,6197,1120],{"class":1119},[1109,6199,3276],{"class":1123},[1109,6201,2859],{"class":2858},[1109,6203,1133],{"class":1132},[1109,6205,3158],{"class":1136},[1109,6207,2867],{"class":1132},[1109,6209,1489],{"class":1119},[1109,6211,3289],{"class":1123},[1109,6213,2859],{"class":2858},[1109,6215,1133],{"class":1132},[1109,6217,3296],{"class":1136},[1109,6219,2867],{"class":1132},[1109,6221,1489],{"class":1119},[1109,6223,3303],{"class":1123},[1109,6225,2859],{"class":2858},[1109,6227,1120],{"class":1119},[1109,6229,3158],{"class":1260},[1109,6231,3206],{"class":1119},[1109,6233,6234,6236,6238,6240,6242,6244,6246,6248,6250,6252,6254,6256,6258,6260,6262,6264,6266,6268,6270,6272,6274,6276,6278,6280,6282,6284,6286,6288,6290,6292,6294,6296,6298,6300,6302,6304,6306],{"class":1111,"line":3314},[1109,6235,3317],{"class":1123},[1109,6237,2859],{"class":2858},[1109,6239,1120],{"class":1119},[1109,6241,3276],{"class":1123},[1109,6243,2859],{"class":2858},[1109,6245,1133],{"class":1132},[1109,6247,3158],{"class":1136},[1109,6249,2867],{"class":1132},[1109,6251,1489],{"class":1119},[1109,6253,3336],{"class":1123},[1109,6255,2859],{"class":2858},[1109,6257,3341],{"class":1119},[1109,6259,1496],{"class":1123},[1109,6261,2859],{"class":2858},[1109,6263,1120],{"class":1119},[1109,6265,3350],{"class":1123},[1109,6267,2859],{"class":2858},[1109,6269,3355],{"class":2877},[1109,6271,1489],{"class":1119},[1109,6273,3289],{"class":1123},[1109,6275,2859],{"class":2858},[1109,6277,1133],{"class":1132},[1109,6279,3366],{"class":1136},[1109,6281,2867],{"class":1132},[1109,6283,1489],{"class":1119},[1109,6285,3373],{"class":1123},[1109,6287,2859],{"class":2858},[1109,6289,3378],{"class":2877},[1109,6291,1489],{"class":1119},[1109,6293,3383],{"class":1123},[1109,6295,2859],{"class":2858},[1109,6297,3388],{"class":2877},[1109,6299,1489],{"class":1119},[1109,6301,3393],{"class":1123},[1109,6303,2859],{"class":2858},[1109,6305,3398],{"class":2877},[1109,6307,3206],{"class":1119},[1109,6309,6310,6312,6314,6316,6318,6320,6322,6324,6326,6328,6330,6332,6334,6336,6338],{"class":1111,"line":3403},[1109,6311,3406],{"class":1123},[1109,6313,2859],{"class":2858},[1109,6315,1120],{"class":1119},[1109,6317,3413],{"class":1123},[1109,6319,2859],{"class":2858},[1109,6321,1133],{"class":1132},[1109,6323,3158],{"class":1136},[1109,6325,2867],{"class":1132},[1109,6327,1489],{"class":1119},[1109,6329,3426],{"class":1123},[1109,6331,2859],{"class":2858},[1109,6333,1133],{"class":1132},[1109,6335,3433],{"class":1136},[1109,6337,2867],{"class":1132},[1109,6339,3231],{"class":1119},[1109,6341,6342],{"class":1111,"line":3440},[1109,6343,3443],{"class":1119},[1109,6345,6346],{"class":1111,"line":3446},[1109,6347,1145],{"emptyLinePlaceholder":8},[1109,6349,6350,6352,6354,6356,6358,6360,6362,6364,6366,6368],{"class":1111,"line":3451},[1109,6351,3454],{"class":1123},[1109,6353,955],{"class":1119},[1109,6355,3459],{"class":1167},[1109,6357,1270],{"class":1119},[1109,6359,2867],{"class":1132},[1109,6361,3466],{"class":1136},[1109,6363,2867],{"class":1132},[1109,6365,1489],{"class":1119},[1109,6367,3473],{"class":1123},[1109,6369,1276],{"class":1119},[1109,6371,6372,6374,6376,6378,6380,6382],{"class":1111,"line":3478},[1109,6373,3454],{"class":1123},[1109,6375,955],{"class":1119},[1109,6377,3485],{"class":1167},[1109,6379,1270],{"class":1119},[1109,6381,3490],{"class":2877},[1109,6383,1276],{"class":1119},[1173,6385,6386],{},[856,6387,3497,6388,3500,6390,3503],{},[860,6389,3466],{},[860,6391,2063],{},[1001,6393,3507],{"id":3506},[856,6395,3510,6396,3514],{},[860,6397,3513],{},[1100,6399,6400],{"className":1102,"code":3517,"language":1104,"meta":1105,"style":1105},[860,6401,6402,6416,6430,6436,6442,6448,6454,6460,6466,6472,6478,6490,6494,6506,6510,6518,6530,6550,6554,6558,6562,6566,6580,6594,6608,6622,6636,6650,6664,6668],{"__ignoreMap":1105},[1109,6403,6404,6406,6408,6410,6412,6414],{"class":1111,"line":1112},[1109,6405,1116],{"class":1115},[1109,6407,3059],{"class":1123},[1109,6409,3062],{"class":1115},[1109,6411,1133],{"class":1132},[1109,6413,3067],{"class":1136},[1109,6415,1139],{"class":1132},[1109,6417,6418,6420,6422,6424,6426,6428],{"class":1111,"line":1142},[1109,6419,1116],{"class":1115},[1109,6421,3540],{"class":1123},[1109,6423,3062],{"class":1115},[1109,6425,1133],{"class":1132},[1109,6427,3547],{"class":1136},[1109,6429,1139],{"class":1132},[1109,6431,6432,6434],{"class":1111,"line":1148},[1109,6433,1116],{"class":1115},[1109,6435,3556],{"class":1119},[1109,6437,6438,6440],{"class":1111,"line":1155},[1109,6439,3561],{"class":1123},[1109,6441,1493],{"class":1119},[1109,6443,6444,6446],{"class":1111,"line":1161},[1109,6445,3568],{"class":1123},[1109,6447,1493],{"class":1119},[1109,6449,6450,6452],{"class":1111,"line":3111},[1109,6451,3575],{"class":1123},[1109,6453,1493],{"class":1119},[1109,6455,6456,6458],{"class":1111,"line":3128},[1109,6457,3582],{"class":1123},[1109,6459,1493],{"class":1119},[1109,6461,6462,6464],{"class":1111,"line":3209},[1109,6463,3589],{"class":1123},[1109,6465,1493],{"class":1119},[1109,6467,6468,6470],{"class":1111,"line":3234},[1109,6469,3596],{"class":1123},[1109,6471,1493],{"class":1119},[1109,6473,6474,6476],{"class":1111,"line":3255},[1109,6475,3603],{"class":1123},[1109,6477,1493],{"class":1119},[1109,6479,6480,6482,6484,6486,6488],{"class":1111,"line":3266},[1109,6481,3610],{"class":1119},[1109,6483,1129],{"class":1115},[1109,6485,1133],{"class":1132},[1109,6487,874],{"class":1136},[1109,6489,1139],{"class":1132},[1109,6491,6492],{"class":1111,"line":3314},[1109,6493,1145],{"emptyLinePlaceholder":8},[1109,6495,6496,6498,6500,6502,6504],{"class":1111,"line":3403},[1109,6497,1253],{"class":1252},[1109,6499,1257],{"class":1256},[1109,6501,1261],{"class":1260},[1109,6503,3059],{"class":1167},[1109,6505,1171],{"class":1119},[1109,6507,6508],{"class":1111,"line":3440},[1109,6509,1145],{"emptyLinePlaceholder":8},[1109,6511,6512,6514,6516],{"class":1111,"line":3446},[1109,6513,1164],{"class":1115},[1109,6515,3645],{"class":1167},[1109,6517,3125],{"class":1119},[1109,6519,6520,6522,6524,6526,6528],{"class":1111,"line":3451},[1109,6521,3131],{"class":1123},[1109,6523,2859],{"class":2858},[1109,6525,1120],{"class":1119},[1109,6527,3158],{"class":1260},[1109,6529,3231],{"class":1119},[1109,6531,6532,6534,6536,6538,6540,6542,6544,6546,6548],{"class":1111,"line":3478},[1109,6533,3212],{"class":1123},[1109,6535,2859],{"class":2858},[1109,6537,1120],{"class":1119},[1109,6539,3219],{"class":1123},[1109,6541,2859],{"class":2858},[1109,6543,1133],{"class":1132},[1109,6545,3158],{"class":1136},[1109,6547,2867],{"class":1132},[1109,6549,3231],{"class":1119},[1109,6551,6552],{"class":1111,"line":3682},[1109,6553,3685],{"class":1151},[1109,6555,6556],{"class":1111,"line":3688},[1109,6557,3443],{"class":1119},[1109,6559,6560],{"class":1111,"line":3693},[1109,6561,1145],{"emptyLinePlaceholder":8},[1109,6563,6564],{"class":1111,"line":3698},[1109,6565,3701],{"class":1151},[1109,6567,6568,6570,6572,6574,6576,6578],{"class":1111,"line":3704},[1109,6569,3707],{"class":1123},[1109,6571,955],{"class":1119},[1109,6573,3459],{"class":1167},[1109,6575,1270],{"class":1119},[1109,6577,3716],{"class":1167},[1109,6579,3719],{"class":1119},[1109,6581,6582,6584,6586,6588,6590,6592],{"class":1111,"line":3722},[1109,6583,3707],{"class":1123},[1109,6585,955],{"class":1119},[1109,6587,3459],{"class":1167},[1109,6589,1270],{"class":1119},[1109,6591,3733],{"class":1167},[1109,6593,3719],{"class":1119},[1109,6595,6596,6598,6600,6602,6604,6606],{"class":1111,"line":3738},[1109,6597,3707],{"class":1123},[1109,6599,955],{"class":1119},[1109,6601,3459],{"class":1167},[1109,6603,1270],{"class":1119},[1109,6605,3749],{"class":1123},[1109,6607,1276],{"class":1119},[1109,6609,6610,6612,6614,6616,6618,6620],{"class":1111,"line":3754},[1109,6611,3707],{"class":1123},[1109,6613,955],{"class":1119},[1109,6615,3459],{"class":1167},[1109,6617,1270],{"class":1119},[1109,6619,3765],{"class":1123},[1109,6621,1276],{"class":1119},[1109,6623,6624,6626,6628,6630,6632,6634],{"class":1111,"line":3770},[1109,6625,3707],{"class":1123},[1109,6627,955],{"class":1119},[1109,6629,3459],{"class":1167},[1109,6631,1270],{"class":1119},[1109,6633,3781],{"class":1123},[1109,6635,1276],{"class":1119},[1109,6637,6638,6640,6642,6644,6646,6648],{"class":1111,"line":3786},[1109,6639,3707],{"class":1123},[1109,6641,955],{"class":1119},[1109,6643,3459],{"class":1167},[1109,6645,1270],{"class":1119},[1109,6647,3797],{"class":1123},[1109,6649,1276],{"class":1119},[1109,6651,6652,6654,6656,6658,6660,6662],{"class":1111,"line":3802},[1109,6653,3707],{"class":1123},[1109,6655,955],{"class":1119},[1109,6657,3459],{"class":1167},[1109,6659,1270],{"class":1119},[1109,6661,3813],{"class":1167},[1109,6663,3719],{"class":1119},[1109,6665,6666],{"class":1111,"line":3818},[1109,6667,1145],{"emptyLinePlaceholder":8},[1109,6669,6670,6672,6674,6676,6678,6680],{"class":1111,"line":3823},[1109,6671,3707],{"class":1123},[1109,6673,955],{"class":1119},[1109,6675,3485],{"class":1167},[1109,6677,1270],{"class":1119},[1109,6679,3490],{"class":2877},[1109,6681,1276],{"class":1119},[1173,6683,6684],{},[856,6685,3840,6686,3843,6688,3846,6690,3849,6692,3852,6694,1489,6696,2513,6698,955],{},[860,6687,3513],{},[860,6689,1387],{},[860,6691,1797],{},[860,6693,1807],{},[860,6695,3855],{},[860,6697,3858],{},[860,6699,3861],{},[1001,6701,3865],{"id":3864},[856,6703,3868,6704,3872,6706,3876],{},[860,6705,3871],{},[860,6707,3875],{},[1100,6709,6710],{"className":1102,"code":3879,"language":1104,"meta":1105,"style":1105},[860,6711,6712,6730,6734],{"__ignoreMap":1105},[1109,6713,6714,6716,6718,6720,6722,6724,6726,6728],{"class":1111,"line":1112},[1109,6715,1116],{"class":1115},[1109,6717,1120],{"class":1119},[1109,6719,3875],{"class":1123},[1109,6721,1126],{"class":1119},[1109,6723,1129],{"class":1115},[1109,6725,1133],{"class":1132},[1109,6727,874],{"class":1136},[1109,6729,1139],{"class":1132},[1109,6731,6732],{"class":1111,"line":1142},[1109,6733,1145],{"emptyLinePlaceholder":8},[1109,6735,6736,6738,6740,6742,6744],{"class":1111,"line":1148},[1109,6737,1164],{"class":1115},[1109,6739,3910],{"class":1167},[1109,6741,1270],{"class":1119},[1109,6743,1273],{"class":1123},[1109,6745,1276],{"class":1119},[856,6747,3919],{},[1100,6749,6750],{"className":3922,"code":3923,"language":3924,"meta":1105,"style":1105},[860,6751,6752],{"__ignoreMap":1105},[1109,6753,6754,6756,6758],{"class":1111,"line":1112},[1109,6755,3931],{"class":1167},[1109,6757,3934],{"class":1136},[1109,6759,3937],{"class":1136},[856,6761,3940,6762,3943,6764,1489,6766,3949],{},[860,6763,3513],{},[860,6765,1510],{},[860,6767,3948],{},[884,6769],{},[887,6771,3955],{"id":3954},[856,6773,3958],{},[895,6775,6776,6790],{},[898,6777,6778],{},[901,6779,6780,6782,6784,6786,6788],{},[904,6781,1860],{},[904,6783,1863],{},[904,6785,3971],{},[904,6787,2596],{},[904,6789,1586],{},[914,6791,6792,6806,6822,6838,6854,6870,6886,6902,6918,6934,6950,6966,6982,6998,7014,7030,7046,7062],{},[901,6793,6794,6796,6800,6802,6804],{},[919,6795,1874],{},[919,6797,6798],{},[860,6799,2890],{},[919,6801,3988],{},[919,6803,3991],{},[919,6805],{},[901,6807,6808,6810,6814,6816,6818],{},[919,6809,1932],{},[919,6811,6812],{},[860,6813,1937],{},[919,6815,4004],{},[919,6817,4007],{},[919,6819,6820],{},[865,6821,99],{"href":100},[901,6823,6824,6826,6830,6832,6834],{},[919,6825,1932],{},[919,6827,6828],{},[860,6829,1962],{},[919,6831,4004],{},[919,6833,4007],{},[919,6835,6836],{},[865,6837,103],{"href":104},[901,6839,6840,6842,6846,6848,6850],{},[919,6841,1932],{},[919,6843,6844],{},[860,6845,1984],{},[919,6847,4004],{},[919,6849,4040],{},[919,6851,6852],{},[865,6853,111],{"href":112},[901,6855,6856,6858,6862,6864,6866],{},[919,6857,1932],{},[919,6859,6860],{},[860,6861,2063],{},[919,6863,4055],{},[919,6865,4058],{},[919,6867,6868],{},[865,6869,91],{"href":92},[901,6871,6872,6874,6878,6880,6882],{},[919,6873,1932],{},[919,6875,6876],{},[860,6877,2093],{},[919,6879,4073],{},[919,6881,4058],{},[919,6883,6884],{},[865,6885,107],{"href":108},[901,6887,6888,6890,6894,6896,6898],{},[919,6889,1874],{},[919,6891,6892],{},[860,6893,2150],{},[919,6895,4090],{},[919,6897,3991],{},[919,6899,6900],{},[865,6901,123],{"href":124},[901,6903,6904,6906,6910,6912,6914],{},[919,6905,1932],{},[919,6907,6908],{},[860,6909,2150],{},[919,6911,4090],{},[919,6913,4007],{},[919,6915,6916],{},[865,6917,123],{"href":124},[901,6919,6920,6922,6926,6928,6930],{},[919,6921,1932],{},[919,6923,6924],{},[860,6925,2181],{},[919,6927,4123],{},[919,6929,4007],{},[919,6931,6932],{},[865,6933,123],{"href":124},[901,6935,6936,6938,6942,6944,6946],{},[919,6937,1874],{},[919,6939,6940],{},[860,6941,2197],{},[919,6943,4123],{},[919,6945,3991],{},[919,6947,6948],{},[865,6949,123],{"href":124},[901,6951,6952,6954,6958,6960,6962],{},[919,6953,1932],{},[919,6955,6956],{},[860,6957,2197],{},[919,6959,4123],{},[919,6961,4007],{},[919,6963,6964],{},[865,6965,123],{"href":124},[901,6967,6968,6970,6974,6976,6978],{},[919,6969,1932],{},[919,6971,6972],{},[860,6973,2228],{},[919,6975,4123],{},[919,6977,4007],{},[919,6979,6980],{},[865,6981,123],{"href":2236},[901,6983,6984,6986,6990,6992,6994],{},[919,6985,1932],{},[919,6987,6988],{},[860,6989,2245],{},[919,6991,3991],{},[919,6993,4007],{},[919,6995,6996],{},[865,6997,163],{"href":164},[901,6999,7000,7002,7006,7008,7010],{},[919,7001,1874],{},[919,7003,7004],{},[860,7005,2261],{},[919,7007,4090],{},[919,7009,3991],{},[919,7011,7012],{},[865,7013,163],{"href":164},[901,7015,7016,7018,7022,7024,7026],{},[919,7017,1932],{},[919,7019,7020],{},[860,7021,2261],{},[919,7023,4090],{},[919,7025,4007],{},[919,7027,7028],{},[865,7029,163],{"href":164},[901,7031,7032,7034,7038,7040,7042],{},[919,7033,1874],{},[919,7035,7036],{},[860,7037,2329],{},[919,7039,4123],{},[919,7041,3991],{},[919,7043,7044],{},[865,7045,2337],{"href":132},[901,7047,7048,7050,7054,7056,7058],{},[919,7049,1874],{},[919,7051,7052],{},[860,7053,2346],{},[919,7055,4123],{},[919,7057,3991],{},[919,7059,7060],{},[865,7061,2337],{"href":132},[901,7063,7064,7066,7070,7072,7074],{},[919,7065,1874],{},[919,7067,7068],{},[860,7069,2556],{},[919,7071,4268],{},[919,7073,3991],{},[919,7075],{},[884,7077],{},[887,7079,4278],{"id":4277},[895,7081,7082,7090],{},[898,7083,7084],{},[901,7085,7086,7088],{},[904,7087,4287],{},[904,7089,4290],{},[914,7091,7092,7104,7116,7126,7134,7142,7150,7158,7166],{},[901,7093,7094,7098],{},[919,7095,7096],{},[1337,7097,4299],{},[919,7099,7100,4304,7102,4307],{},[860,7101,874],{},[860,7103,862],{},[901,7105,7106,7112],{},[919,7107,7108],{},[1337,7109,7110],{},[860,7111,950],{},[919,7113,4318,7114,4321],{},[860,7115,881],{},[901,7117,7118,7124],{},[919,7119,7120],{},[1337,7121,7122],{},[860,7123,881],{},[919,7125,4332],{},[901,7127,7128,7132],{},[919,7129,7130],{},[1337,7131,4339],{},[919,7133,4342],{},[901,7135,7136,7140],{},[919,7137,7138],{},[1337,7139,4349],{},[919,7141,4352],{},[901,7143,7144,7148],{},[919,7145,7146],{},[1337,7147,4359],{},[919,7149,4362],{},[901,7151,7152,7156],{},[919,7153,7154],{},[1337,7155,4369],{},[919,7157,4372],{},[901,7159,7160,7164],{},[919,7161,7162],{},[1337,7163,4379],{},[919,7165,4382],{},[901,7167,7168,7172],{},[919,7169,7170],{},[1337,7171,4389],{},[919,7173,7174,4394,7176,4397],{},[860,7175,1432],{},[860,7177,1436],{},[4399,7179,4401],{},{"title":1105,"searchDepth":1142,"depth":1142,"links":7181},[7182,7183,7186,7189,7190,7199,7200,7201,7206,7207,7212,7213],{"id":889,"depth":1142,"text":890},{"id":991,"depth":1142,"text":950,"children":7184},[7185],{"id":1003,"depth":1148,"text":1004},{"id":1186,"depth":1142,"text":881,"children":7187},[7188],{"id":1279,"depth":1148,"text":1280},{"id":1565,"depth":1142,"text":1566},{"id":1840,"depth":1142,"text":1841,"children":7191},[7192,7193,7194,7195,7196,7197,7198],{"id":1847,"depth":1148,"text":1848},{"id":1905,"depth":1148,"text":1906},{"id":2032,"depth":1148,"text":2033},{"id":2121,"depth":1148,"text":2122},{"id":2300,"depth":1148,"text":2301},{"id":2369,"depth":1148,"text":2370},{"id":2529,"depth":1148,"text":2530},{"id":2577,"depth":1142,"text":2578},{"id":2644,"depth":1142,"text":237},{"id":2657,"depth":1142,"text":2658,"children":7202},[7203,7204,7205],{"id":2664,"depth":1148,"text":2665},{"id":2757,"depth":1148,"text":2758},{"id":2828,"depth":1148,"text":2829},{"id":2929,"depth":1142,"text":2930},{"id":3025,"depth":1142,"text":3026,"children":7208},[7209,7210,7211],{"id":3041,"depth":1148,"text":4432},{"id":3506,"depth":1148,"text":3507},{"id":3864,"depth":1148,"text":3865},{"id":3954,"depth":1142,"text":3955},{"id":4277,"depth":1142,"text":4278},{},{"title":159,"description":4437},1780436284000]