[{"data":1,"prerenderedAt":2368},["ShallowReactive",2],{"navigation_docs":3,"-use-cases-telemetry-ingest":598,"-use-cases-telemetry-ingest-surround":2363},[4,35,85,138,318,453,475,534,573],{"title":5,"path":6,"stem":7,"children":8,"page":34},"Start","\u002Fstart","1.start",[9,14,19,24,29],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fstart\u002Fintroduction","1.start\u002F1.introduction","i-lucide-info",{"title":15,"path":16,"stem":17,"icon":18},"Why start with evlog","\u002Fstart\u002Fwhy-evlog","1.start\u002F2.why-evlog","i-lucide-rocket",{"title":20,"path":21,"stem":22,"icon":23},"Installation","\u002Fstart\u002Finstallation","1.start\u002F3.installation","i-lucide-download",{"title":25,"path":26,"stem":27,"icon":28},"Quick Start","\u002Fstart\u002Fquick-start","1.start\u002F4.quick-start","i-lucide-zap",{"title":30,"path":31,"stem":32,"icon":33},"Choose your logger","\u002Fstart\u002Ftypescript-logging-library","1.start\u002F5.typescript-logging-library","i-lucide-file-code",false,{"title":36,"path":37,"stem":38,"children":39,"page":34},"Learn","\u002Flearn","2.learn",[40,45,50,55,60,65,70,75,80],{"title":41,"path":42,"stem":43,"icon":44},"Overview","\u002Flearn\u002Foverview","2.learn\u002F0.overview","i-lucide-list",{"title":46,"path":47,"stem":48,"icon":49},"Simple Logging","\u002Flearn\u002Fsimple-logging","2.learn\u002F1.simple-logging","i-lucide-terminal",{"title":51,"path":52,"stem":53,"icon":54},"Wide Events","\u002Flearn\u002Fwide-events","2.learn\u002F2.wide-events","i-lucide-layers",{"title":56,"path":57,"stem":58,"icon":59},"Structured Errors","\u002Flearn\u002Fstructured-errors","2.learn\u002F3.structured-errors","i-lucide-shield-alert",{"title":61,"path":62,"stem":63,"icon":64},"Lifecycle","\u002Flearn\u002Flifecycle","2.learn\u002F4.lifecycle","i-lucide-arrow-right-left",{"title":66,"path":67,"stem":68,"icon":69},"Sampling","\u002Flearn\u002Fsampling","2.learn\u002F5.sampling","i-lucide-filter",{"title":71,"path":72,"stem":73,"icon":74},"Auto-Redaction","\u002Flearn\u002Fredaction","2.learn\u002F6.redaction","i-lucide-eye-off",{"title":76,"path":77,"stem":78,"icon":79},"Typed Fields","\u002Flearn\u002Ftyped-fields","2.learn\u002F7.typed-fields","i-simple-icons-typescript",{"title":81,"path":82,"stem":83,"icon":84},"Catalogs","\u002Flearn\u002Fcatalogs","2.learn\u002F8.catalogs","i-lucide-book-open",{"title":86,"path":87,"stem":88,"children":89,"page":34},"CLI","\u002Fcli","3.cli",[90,93,98,103,108,113,118,123,128,133],{"title":41,"path":91,"stem":92,"icon":49},"\u002Fcli\u002Foverview","3.cli\u002F0.overview",{"title":94,"path":95,"stem":96,"icon":97},"init","\u002Fcli\u002Finit","3.cli\u002F1.init","i-lucide-wand-sparkles",{"title":99,"path":100,"stem":101,"icon":102},"map","\u002Fcli\u002Fmap","3.cli\u002F2.map","i-lucide-radar",{"title":104,"path":105,"stem":106,"icon":107},"Rules","\u002Fcli\u002Frules","3.cli\u002F3.rules","i-lucide-list-checks",{"title":109,"path":110,"stem":111,"icon":112},"Scoring","\u002Fcli\u002Fscoring","3.cli\u002F4.scoring","i-lucide-gauge",{"title":114,"path":115,"stem":116,"icon":117},"CI","\u002Fcli\u002Fci","3.cli\u002F5.ci","i-lucide-git-pull-request",{"title":119,"path":120,"stem":121,"icon":122},"doctor","\u002Fcli\u002Fdoctor","3.cli\u002F6.doctor","i-lucide-stethoscope",{"title":124,"path":125,"stem":126,"icon":127},"telemetry","\u002Fcli\u002Ftelemetry","3.cli\u002F7.telemetry","i-lucide-bar-chart-3",{"title":129,"path":130,"stem":131,"icon":132},"agents","\u002Fcli\u002Fagents","3.cli\u002F8.agents","i-lucide-bot",{"title":134,"path":135,"stem":136,"icon":137},"Observability Score","\u002Fcli\u002Fobservability-score","3.cli\u002F9.observability-score","i-lucide-activity",{"title":139,"path":140,"stem":141,"children":142,"page":34},"Integrate","\u002Fintegrate","4.integrate",[143,147,230],{"title":41,"path":144,"stem":145,"icon":146},"\u002Fintegrate\u002Foverview","4.integrate\u002F0.overview","i-lucide-plug",{"title":148,"path":149,"stem":150,"children":151,"page":34},"Adapters","\u002Fintegrate\u002Fadapters","4.integrate\u002Fadapters",[152,155,185,210],{"title":41,"path":153,"stem":154,"icon":44},"\u002Fintegrate\u002Fadapters\u002Foverview","4.integrate\u002Fadapters\u002F01.overview",{"title":156,"path":157,"stem":158,"children":159,"page":34},"Cloud","\u002Fintegrate\u002Fadapters\u002Fcloud","4.integrate\u002Fadapters\u002Fcloud",[160,165,170,175,180],{"title":161,"path":162,"stem":163,"icon":164},"Axiom","\u002Fintegrate\u002Fadapters\u002Fcloud\u002Faxiom","4.integrate\u002Fadapters\u002Fcloud\u002F01.axiom","i-custom-axiom",{"title":166,"path":167,"stem":168,"icon":169},"PostHog","\u002Fintegrate\u002Fadapters\u002Fcloud\u002Fposthog","4.integrate\u002Fadapters\u002Fcloud\u002F02.posthog","i-simple-icons-posthog",{"title":171,"path":172,"stem":173,"icon":174},"Sentry","\u002Fintegrate\u002Fadapters\u002Fcloud\u002Fsentry","4.integrate\u002Fadapters\u002Fcloud\u002F03.sentry","i-simple-icons-sentry",{"title":176,"path":177,"stem":178,"icon":179},"Better Stack","\u002Fintegrate\u002Fadapters\u002Fcloud\u002Fbetter-stack","4.integrate\u002Fadapters\u002Fcloud\u002F04.better-stack","i-simple-icons-betterstack",{"title":181,"path":182,"stem":183,"icon":184},"Datadog","\u002Fintegrate\u002Fadapters\u002Fcloud\u002Fdatadog","4.integrate\u002Fadapters\u002Fcloud\u002F05.datadog","i-simple-icons-datadog",{"title":186,"path":187,"stem":188,"children":189,"page":34},"Cloud or Self-Hosted","\u002Fintegrate\u002Fadapters\u002Fhybrid","4.integrate\u002Fadapters\u002Fhybrid",[190,195,200,205],{"title":191,"path":192,"stem":193,"icon":194},"Grafana Loki","\u002Fintegrate\u002Fadapters\u002Fhybrid\u002Floki","4.integrate\u002Fadapters\u002Fhybrid\u002F01.loki","i-simple-icons-grafana",{"title":196,"path":197,"stem":198,"icon":199},"ClickHouse","\u002Fintegrate\u002Fadapters\u002Fhybrid\u002Fclickhouse","4.integrate\u002Fadapters\u002Fhybrid\u002F02.clickhouse","i-simple-icons-clickhouse",{"title":201,"path":202,"stem":203,"icon":204},"OTLP","\u002Fintegrate\u002Fadapters\u002Fhybrid\u002Fotlp","4.integrate\u002Fadapters\u002Fhybrid\u002F03.otlp","i-simple-icons-opentelemetry",{"title":206,"path":207,"stem":208,"icon":209},"HyperDX","\u002Fintegrate\u002Fadapters\u002Fhybrid\u002Fhyperdx","4.integrate\u002Fadapters\u002Fhybrid\u002F04.hyperdx","i-custom-hyperdx",{"title":211,"path":212,"stem":213,"children":214,"page":34},"Self-Hosted","\u002Fintegrate\u002Fadapters\u002Fself-hosted","4.integrate\u002Fadapters\u002Fself-hosted",[215,220,225],{"title":216,"path":217,"stem":218,"icon":219},"File System","\u002Fintegrate\u002Fadapters\u002Fself-hosted\u002Ffs","4.integrate\u002Fadapters\u002Fself-hosted\u002F01.fs","i-lucide-hard-drive",{"title":221,"path":222,"stem":223,"icon":224},"NuxtHub","\u002Fintegrate\u002Fadapters\u002Fself-hosted\u002Fnuxthub","4.integrate\u002Fadapters\u002Fself-hosted\u002F02.nuxthub","i-simple-icons-nuxt",{"title":226,"path":227,"stem":228,"icon":229},"Memory","\u002Fintegrate\u002Fadapters\u002Fself-hosted\u002Fmemory","4.integrate\u002Fadapters\u002Fself-hosted\u002F03.memory","i-lucide-cpu",{"title":231,"path":232,"stem":233,"children":234,"page":34},"Frameworks","\u002Fintegrate\u002Fframeworks","4.integrate\u002Fframeworks",[235,239,244,249,254,259,264,269,274,279,284,289,294,299,303,308,313],{"title":41,"path":236,"stem":237,"icon":238},"\u002Fintegrate\u002Fframeworks\u002Foverview","4.integrate\u002Fframeworks\u002F00.overview","i-lucide-layout-grid",{"title":240,"path":241,"stem":242,"icon":243},"Nuxt","\u002Fintegrate\u002Fframeworks\u002Fnuxt","4.integrate\u002Fframeworks\u002F01.nuxt","i-simple-icons-nuxtdotjs",{"title":245,"path":246,"stem":247,"icon":248},"Next.js","\u002Fintegrate\u002Fframeworks\u002Fnextjs","4.integrate\u002Fframeworks\u002F02.nextjs","i-simple-icons-nextdotjs",{"title":250,"path":251,"stem":252,"icon":253},"SvelteKit","\u002Fintegrate\u002Fframeworks\u002Fsveltekit","4.integrate\u002Fframeworks\u002F03.sveltekit","i-simple-icons-svelte",{"title":255,"path":256,"stem":257,"icon":258},"Nitro","\u002Fintegrate\u002Fframeworks\u002Fnitro","4.integrate\u002Fframeworks\u002F04.nitro","i-custom-nitro",{"title":260,"path":261,"stem":262,"icon":263},"TanStack Start","\u002Fintegrate\u002Fframeworks\u002Ftanstack-start","4.integrate\u002Fframeworks\u002F05.tanstack-start","i-custom-tanstack",{"title":265,"path":266,"stem":267,"icon":268},"NestJS","\u002Fintegrate\u002Fframeworks\u002Fnestjs","4.integrate\u002Fframeworks\u002F06.nestjs","i-simple-icons-nestjs",{"title":270,"path":271,"stem":272,"icon":273},"Express","\u002Fintegrate\u002Fframeworks\u002Fexpress","4.integrate\u002Fframeworks\u002F07.express","i-simple-icons-express",{"title":275,"path":276,"stem":277,"icon":278},"Hono","\u002Fintegrate\u002Fframeworks\u002Fhono","4.integrate\u002Fframeworks\u002F08.hono","i-simple-icons-hono",{"title":280,"path":281,"stem":282,"icon":283},"Fastify","\u002Fintegrate\u002Fframeworks\u002Ffastify","4.integrate\u002Fframeworks\u002F09.fastify","i-simple-icons-fastify",{"title":285,"path":286,"stem":287,"icon":288},"Elysia","\u002Fintegrate\u002Fframeworks\u002Felysia","4.integrate\u002Fframeworks\u002F10.elysia","i-custom-elysia",{"title":290,"path":291,"stem":292,"icon":293},"React Router","\u002Fintegrate\u002Fframeworks\u002Freact-router","4.integrate\u002Fframeworks\u002F11.react-router","i-custom-reactrouter",{"title":295,"path":296,"stem":297,"icon":298},"Cloudflare Workers","\u002Fintegrate\u002Fframeworks\u002Fcloudflare-workers","4.integrate\u002Fframeworks\u002F12.cloudflare-workers","i-simple-icons-cloudflare",{"title":300,"path":301,"stem":302,"icon":79},"Standalone","\u002Fintegrate\u002Fframeworks\u002Fstandalone","4.integrate\u002Fframeworks\u002F13.standalone",{"title":304,"path":305,"stem":306,"icon":307},"Astro","\u002Fintegrate\u002Fframeworks\u002Fastro","4.integrate\u002Fframeworks\u002F14.astro","i-simple-icons-astro",{"title":309,"path":310,"stem":311,"icon":312},"oRPC","\u002Fintegrate\u002Fframeworks\u002Forpc","4.integrate\u002Fframeworks\u002F15.orpc","i-lucide-network",{"title":314,"path":315,"stem":316,"icon":317},"AWS Lambda","\u002Fintegrate\u002Fframeworks\u002Faws-lambda","4.integrate\u002Fframeworks\u002F16.aws-lambda","i-custom-lambda",{"title":319,"path":320,"stem":321,"children":322,"page":34},"Use Cases","\u002Fuse-cases","5.use-cases",[323,326,331,359,386,418,439,444,449],{"title":41,"path":324,"stem":325,"icon":107},"\u002Fuse-cases\u002Foverview","5.use-cases\u002F0.overview",{"title":327,"path":328,"stem":329,"icon":330},"Client Logging","\u002Fuse-cases\u002Fclient-logging","5.use-cases\u002F1.client-logging","i-lucide-monitor",{"title":332,"icon":333,"path":334,"stem":335,"children":336,"page":34},"AI SDK","i-simple-icons-vercel","\u002Fuse-cases\u002Fai-sdk","5.use-cases\u002F2.ai-sdk",[337,340,345,350,355],{"title":41,"path":338,"stem":339,"icon":44},"\u002Fuse-cases\u002Fai-sdk\u002Foverview","5.use-cases\u002F2.ai-sdk\u002F01.overview",{"title":341,"path":342,"stem":343,"icon":344},"Usage","\u002Fuse-cases\u002Fai-sdk\u002Fusage","5.use-cases\u002F2.ai-sdk\u002F02.usage","i-lucide-code",{"title":346,"path":347,"stem":348,"icon":349},"Options","\u002Fuse-cases\u002Fai-sdk\u002Foptions","5.use-cases\u002F2.ai-sdk\u002F03.options","i-lucide-sliders",{"title":351,"path":352,"stem":353,"icon":354},"Metadata","\u002Fuse-cases\u002Fai-sdk\u002Fmetadata","5.use-cases\u002F2.ai-sdk\u002F04.metadata","i-lucide-database",{"title":356,"path":357,"stem":358,"icon":137},"Telemetry","\u002Fuse-cases\u002Fai-sdk\u002Ftelemetry","5.use-cases\u002F2.ai-sdk\u002F05.telemetry",{"title":360,"icon":361,"path":362,"stem":363,"children":364,"page":34},"Better Auth","i-simple-icons-betterauth","\u002Fuse-cases\u002Fbetter-auth","5.use-cases\u002F3.better-auth",[365,368,373,378,382],{"title":41,"path":366,"stem":367,"icon":44},"\u002Fuse-cases\u002Fbetter-auth\u002Foverview","5.use-cases\u002F3.better-auth\u002F01.overview",{"title":369,"path":370,"stem":371,"icon":372},"Identify User","\u002Fuse-cases\u002Fbetter-auth\u002Fidentify-user","5.use-cases\u002F3.better-auth\u002F02.identify-user","i-lucide-user-check",{"title":374,"path":375,"stem":376,"icon":377},"Middleware","\u002Fuse-cases\u002Fbetter-auth\u002Fmiddleware","5.use-cases\u002F3.better-auth\u002F03.middleware","i-lucide-shield",{"title":379,"path":380,"stem":381,"icon":330},"Client Sync","\u002Fuse-cases\u002Fbetter-auth\u002Fclient-sync","5.use-cases\u002F3.better-auth\u002F04.client-sync",{"title":383,"path":384,"stem":385,"icon":112},"Performance","\u002Fuse-cases\u002Fbetter-auth\u002Fperformance","5.use-cases\u002F3.better-auth\u002F05.performance",{"title":387,"icon":388,"path":389,"stem":390,"children":391,"page":34},"Audit Logs","i-lucide-shield-check","\u002Fuse-cases\u002Faudit","5.use-cases\u002F4.audit",[392,395,400,405,410,414],{"title":41,"path":393,"stem":394,"icon":44},"\u002Fuse-cases\u002Faudit\u002Foverview","5.use-cases\u002F4.audit\u002F01.overview",{"title":396,"path":397,"stem":398,"icon":399},"Schema","\u002Fuse-cases\u002Faudit\u002Fschema","5.use-cases\u002F4.audit\u002F02.schema","i-lucide-file-text",{"title":401,"path":402,"stem":403,"icon":404},"Recording","\u002Fuse-cases\u002Faudit\u002Frecording","5.use-cases\u002F4.audit\u002F03.recording","i-lucide-pen-line",{"title":406,"path":407,"stem":408,"icon":409},"Drains","\u002Fuse-cases\u002Faudit\u002Fpipeline","5.use-cases\u002F4.audit\u002F04.pipeline","i-lucide-link",{"title":411,"path":412,"stem":413,"icon":388},"Compliance","\u002Fuse-cases\u002Faudit\u002Fcompliance","5.use-cases\u002F4.audit\u002F05.compliance",{"title":415,"path":416,"stem":417,"icon":84},"Recipes","\u002Fuse-cases\u002Faudit\u002Frecipes","5.use-cases\u002F4.audit\u002F06.recipes",{"title":356,"icon":112,"path":419,"stem":420,"children":421,"page":34},"\u002Fuse-cases\u002Ftelemetry","5.use-cases\u002F4.telemetry",[422,425,430,435],{"title":41,"path":423,"stem":424,"icon":44},"\u002Fuse-cases\u002Ftelemetry\u002Foverview","5.use-cases\u002F4.telemetry\u002F01.overview",{"title":426,"path":427,"stem":428,"icon":429},"Setup","\u002Fuse-cases\u002Ftelemetry\u002Fsetup","5.use-cases\u002F4.telemetry\u002F02.setup","i-lucide-wrench",{"title":431,"path":432,"stem":433,"icon":434},"Ingest","\u002Fuse-cases\u002Ftelemetry\u002Fingest","5.use-cases\u002F4.telemetry\u002F03.ingest","i-lucide-server",{"title":436,"path":437,"stem":438,"icon":84},"Reference","\u002Fuse-cases\u002Ftelemetry\u002Freference","5.use-cases\u002F4.telemetry\u002F04.reference",{"title":440,"path":441,"stem":442,"icon":443},"Enrichers","\u002Fuse-cases\u002Fenrichers","5.use-cases\u002F5.enrichers","i-lucide-sparkles",{"title":445,"path":446,"stem":447,"icon":448},"eve","\u002Fuse-cases\u002Feve","5.use-cases\u002F5.eve","i-custom-eve",{"title":450,"path":451,"stem":452,"icon":434},"Node.js request logging","\u002Fuse-cases\u002Fstructured-logging-nodejs","5.use-cases\u002F6.structured-logging-nodejs",{"title":454,"path":455,"stem":456,"children":457,"page":34},"Libraries","\u002Flibraries","6.libraries",[458,462,467,470],{"title":41,"path":459,"stem":460,"icon":461},"\u002Flibraries\u002Foverview","6.libraries\u002F0.overview","i-lucide-package",{"title":463,"path":464,"stem":465,"icon":466},"Emitting Events","\u002Flibraries\u002Femitting","6.libraries\u002F1.emitting","i-lucide-radio",{"title":56,"path":468,"stem":469,"icon":59},"\u002Flibraries\u002Ferrors","6.libraries\u002F2.errors",{"title":471,"path":472,"stem":473,"icon":474},"Testing","\u002Flibraries\u002Ftesting","6.libraries\u002F3.testing","i-lucide-flask-conical",{"title":476,"path":477,"stem":478,"children":479,"page":34},"Extend","\u002Fextend","7.extend",[480,484,489,494,498,503,507,511,515,519,524,529],{"title":41,"path":481,"stem":482,"icon":483},"\u002Fextend\u002Foverview","7.extend\u002F0.overview","i-lucide-blocks",{"title":485,"path":486,"stem":487,"icon":488},"Stream","\u002Fextend\u002Fstream","7.extend\u002F1.stream","i-lucide-radio-tower",{"title":490,"path":491,"stem":492,"icon":493},"Custom framework","\u002Fextend\u002Fcustom-framework","7.extend\u002F10.custom-framework","i-lucide-puzzle",{"title":495,"path":496,"stem":497,"icon":488},"Diagnostics channel","\u002Fextend\u002Fdiagnostics-channel","7.extend\u002F11.diagnostics-channel",{"title":499,"path":500,"stem":501,"icon":502},"FS reader","\u002Fextend\u002Ffs-reader","7.extend\u002F2.fs-reader","i-lucide-folder-search",{"title":415,"path":504,"stem":505,"icon":506},"\u002Fextend\u002Fconsumer-recipes","7.extend\u002F3.consumer-recipes","i-lucide-chef-hat",{"title":508,"path":509,"stem":510,"icon":483},"Plugins","\u002Fextend\u002Fplugins","7.extend\u002F4.plugins",{"title":512,"path":513,"stem":514,"icon":443},"Custom enrichers","\u002Fextend\u002Fcustom-enrichers","7.extend\u002F5.custom-enrichers",{"title":516,"path":517,"stem":518,"icon":69},"Tail sampling","\u002Fextend\u002Ftail-sampling","7.extend\u002F6.tail-sampling",{"title":520,"path":521,"stem":522,"icon":523},"Identity headers","\u002Fextend\u002Fidentity-headers","7.extend\u002F7.identity-headers","i-lucide-fingerprint",{"title":525,"path":526,"stem":527,"icon":528},"Custom drains","\u002Fextend\u002Fcustom-drains","7.extend\u002F8.custom-drains","i-lucide-share-2",{"title":530,"path":531,"stem":532,"icon":533},"Drain pipeline","\u002Fextend\u002Fdrain-pipeline","7.extend\u002F9.drain-pipeline","i-lucide-workflow",{"title":436,"path":535,"stem":536,"children":537,"page":34},"\u002Freference","8.reference",[538,542,547,550,555,559,564,568],{"title":41,"path":539,"stem":540,"icon":541},"\u002Freference\u002Foverview","8.reference\u002F0.overview","i-lucide-book-marked",{"title":543,"path":544,"stem":545,"icon":546},"Configuration","\u002Freference\u002Fconfiguration","8.reference\u002F1.configuration","i-lucide-settings",{"title":383,"path":548,"stem":549,"icon":112},"\u002Freference\u002Fperformance","8.reference\u002F2.performance",{"title":551,"path":552,"stem":553,"icon":554},"Vite Plugin","\u002Freference\u002Fvite-plugin","8.reference\u002F3.vite-plugin","i-custom-vite",{"title":556,"path":557,"stem":558,"icon":388},"Best Practices","\u002Freference\u002Fbest-practices","8.reference\u002F4.best-practices",{"title":560,"path":561,"stem":562,"icon":563},"vs Other Loggers","\u002Freference\u002Fvs-other-loggers","8.reference\u002F5.vs-other-loggers","i-lucide-scale",{"title":565,"path":566,"stem":567,"icon":443},"Agent Skills","\u002Freference\u002Fagent-skills","8.reference\u002F6.agent-skills",{"title":569,"path":570,"stem":571,"icon":572},"Cost","\u002Freference\u002Fcost","8.reference\u002F7.cost","i-lucide-receipt",{"title":574,"path":575,"stem":576,"children":577,"page":34},"Compare","\u002Fcompare","9.compare",[578,582,586,590,594],{"title":579,"path":580,"stem":581,"icon":563},"evlog vs Pino","\u002Fcompare\u002Fpino","9.compare\u002F1.pino",{"title":583,"path":584,"stem":585,"icon":204},"evlog vs OpenTelemetry","\u002Fcompare\u002Fopentelemetry","9.compare\u002F2.opentelemetry",{"title":587,"path":588,"stem":589,"icon":563},"evlog vs LogTape","\u002Fcompare\u002Flogtape","9.compare\u002F3.logtape",{"title":591,"path":592,"stem":593,"icon":563},"evlog vs Winston: formats and request events","\u002Fcompare\u002Fwinston","9.compare\u002F4.winston",{"title":595,"path":596,"stem":597,"icon":563},"evlog vs Consola: reporters and request context","\u002Fcompare\u002Fconsola","9.compare\u002F5.consola",{"id":599,"title":600,"body":601,"description":2351,"extension":2352,"links":2353,"meta":2359,"navigation":2360,"path":432,"seo":2361,"stem":433,"__hash__":2362},"docs\u002F5.use-cases\u002F4.telemetry\u002F03.ingest.md","Telemetry Ingest",{"type":602,"value":603,"toc":2343},"minimark",[604,618,626,631,638,643,654,659,670,765,771,785,791,803,1032,1051,1055,1062,1998,2002,2008,2316,2320,2329,2339],[605,606,607,608,612,613,617],"p",{},"You ship a CLI. Users run it on their machines. You want to know ",[609,610,611],"em",{},"which commands run, how often they fail, and which versions are out there",", without building a separate analytics SDK or reading raw ",[614,615,616],"code",{},"argv",".",[605,619,620,621,625],{},"That means a ",[622,623,624],"strong",{},"server endpoint"," your CLI POSTs to. The local outbox is only there so short CI jobs and offline runs do not lose data before the POST succeeds.",[627,628,630],"h2",{"id":629},"the-security-question-read-this-first","The security question (read this first)",[632,633,634,637],"tip",{},[622,635,636],{},"CLI telemetry is not a vault."," You are collecting anonymous usage counters (command name, duration, outcome, a few numbers), not passwords, not file paths, not tokens. Design for that threat model and you do not need impossible client-side secrets.",[605,639,640],{},[622,641,642],{},"\"I put the ingest URL in my CLI. Isn't that a secret?\"",[605,644,645,646,649,650,653],{},"No. The URL lives in your npm package or binary. Anyone can read it with ",[614,647,648],{},"strings",", by installing the package, or by watching network traffic. The same applies to an API key or ",[614,651,652],{},"Authorization"," header baked into the CLI is extractable, not a real barrier.",[605,655,656],{},[622,657,658],{},"\"So can't anyone send fake events?\"",[605,660,661,662,665,666,669],{},"In theory, yes: someone could ",[614,663,664],{},"curl"," your endpoint with forged JSON. That is why you treat the endpoint as ",[622,667,668],{},"semi-public"," (like a browser analytics key) and defend on the server:",[671,672,673,686],"table",{},[674,675,676],"thead",{},[677,678,679,683],"tr",{},[680,681,682],"th",{},"What you might worry about",[680,684,685],{},"Practical answer",[687,688,689,701,712,723,738,750],"tbody",{},[677,690,691,695],{},[692,693,694],"td",{},"Random internet bots",[692,696,697,698],{},"Validate the payload shape; reject garbage with ",[614,699,700],{},"400",[677,702,703,709],{},[692,704,705,706,708],{},"Someone spamming fake ",[614,707,119],{}," runs",[692,710,711],{},"Rate-limit by IP; data is low-value counters, not money",[677,713,714,720],{},[692,715,716,717],{},"Forged ",[614,718,719],{},"tool.name",[692,721,722],{},"Allowlist only your published tool names server-side",[677,724,725,731],{},[692,726,727,728],{},"Unexpected fields in ",[614,729,730],{},"custom",[692,732,733,734,737],{},"Filter to the keys you declared — same list as ",[614,735,736],{},"collect"," on the CLI",[677,739,740,743],{},[692,741,742],{},"Double-counting on retry",[692,744,745,746,749],{},"Dedupe on ",[614,747,748],{},"idempotencyKey"," when storing",[677,751,752,755],{},[692,753,754],{},"Leaking user secrets",[692,756,757,758,760,761,764],{},"CLI never reads raw ",[614,759,616],{},"; you only allowlisted flags and ",[614,762,763],{},"telemetry.set()"," counters",[605,766,767,770],{},[622,768,769],{},"What \"good enough\" looks like:"," validate every POST, store idempotently, rate-limit at the edge (CDN, API gateway, or middleware), and keep the payload boring (no PII by design). That is the same playbook as client-side analytics: not banking-grade auth, but reliable product insight.",[772,773,775,778,779,784],"callout",{"color":774,"icon":388},"info",[614,776,777],{},"@evlog\u002Ftelemetry"," ships ",[622,780,781],{},[614,782,783],{},"parseIngestBody()"," for your server, with the same validation rules documented here, so you do not copy-paste a validator from the docs. Pair it with your framework route and your database or evlog drain.",[627,786,788,789],{"id":787},"step-1-validate-with-parseingestbody","Step 1: Validate with ",[614,790,783],{},[605,792,793,794,797,798,800,801,617],{},"Mirror the CLI config on the server: same ",[614,795,796],{},"name",", same custom keys you allow via ",[614,799,736],{}," \u002F ",[614,802,763],{},[804,805,811],"pre",{"className":806,"code":807,"filename":808,"language":809,"meta":810,"style":810},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { parseIngestBody } from '@evlog\u002Ftelemetry\u002Fingest'\n\nexport const ingestOptions = {\n  allowedTools: ['my-tool'],\n  allowedCustomKeys: {\n    'my-tool': ['checksFailed', 'checksWarn', 'itemsSynced'],\n  },\n} as const\n\nexport function parseTelemetryBody(raw: string) {\n  return parseIngestBody(raw, ingestOptions)\n}\n","lib\u002Ftelemetry-ingest.ts","ts","",[614,812,813,846,853,872,899,909,953,959,971,976,1006,1026],{"__ignoreMap":810},[814,815,818,822,826,830,833,836,839,843],"span",{"class":816,"line":817},"line",1,[814,819,821],{"class":820},"s7zQu","import",[814,823,825],{"class":824},"sMK4o"," {",[814,827,829],{"class":828},"sTEyZ"," parseIngestBody",[814,831,832],{"class":824}," }",[814,834,835],{"class":820}," from",[814,837,838],{"class":824}," '",[814,840,842],{"class":841},"sfazB","@evlog\u002Ftelemetry\u002Fingest",[814,844,845],{"class":824},"'\n",[814,847,849],{"class":816,"line":848},2,[814,850,852],{"emptyLinePlaceholder":851},true,"\n",[814,854,856,859,863,866,869],{"class":816,"line":855},3,[814,857,858],{"class":820},"export",[814,860,862],{"class":861},"spNyl"," const",[814,864,865],{"class":828}," ingestOptions ",[814,867,868],{"class":824},"=",[814,870,871],{"class":824}," {\n",[814,873,875,879,882,885,888,891,893,896],{"class":816,"line":874},4,[814,876,878],{"class":877},"swJcz","  allowedTools",[814,880,881],{"class":824},":",[814,883,884],{"class":828}," [",[814,886,887],{"class":824},"'",[814,889,890],{"class":841},"my-tool",[814,892,887],{"class":824},[814,894,895],{"class":828},"]",[814,897,898],{"class":824},",\n",[814,900,902,905,907],{"class":816,"line":901},5,[814,903,904],{"class":877},"  allowedCustomKeys",[814,906,881],{"class":824},[814,908,871],{"class":824},[814,910,912,915,917,919,921,923,925,928,930,933,935,938,940,942,944,947,949,951],{"class":816,"line":911},6,[814,913,914],{"class":824},"    '",[814,916,890],{"class":877},[814,918,887],{"class":824},[814,920,881],{"class":824},[814,922,884],{"class":828},[814,924,887],{"class":824},[814,926,927],{"class":841},"checksFailed",[814,929,887],{"class":824},[814,931,932],{"class":824},",",[814,934,838],{"class":824},[814,936,937],{"class":841},"checksWarn",[814,939,887],{"class":824},[814,941,932],{"class":824},[814,943,838],{"class":824},[814,945,946],{"class":841},"itemsSynced",[814,948,887],{"class":824},[814,950,895],{"class":828},[814,952,898],{"class":824},[814,954,956],{"class":816,"line":955},7,[814,957,958],{"class":824},"  },\n",[814,960,962,965,968],{"class":816,"line":961},8,[814,963,964],{"class":824},"}",[814,966,967],{"class":820}," as",[814,969,970],{"class":861}," const\n",[814,972,974],{"class":816,"line":973},9,[814,975,852],{"emptyLinePlaceholder":851},[814,977,979,981,984,988,991,995,997,1001,1004],{"class":816,"line":978},10,[814,980,858],{"class":820},[814,982,983],{"class":861}," function",[814,985,987],{"class":986},"s2Zo4"," parseTelemetryBody",[814,989,990],{"class":824},"(",[814,992,994],{"class":993},"sHdIc","raw",[814,996,881],{"class":824},[814,998,1000],{"class":999},"sBMFI"," string",[814,1002,1003],{"class":824},")",[814,1005,871],{"class":824},[814,1007,1009,1012,1014,1016,1018,1020,1023],{"class":816,"line":1008},11,[814,1010,1011],{"class":820},"  return",[814,1013,829],{"class":986},[814,1015,990],{"class":877},[814,1017,994],{"class":828},[814,1019,932],{"class":824},[814,1021,1022],{"class":828}," ingestOptions",[814,1024,1025],{"class":877},")\n",[814,1027,1029],{"class":816,"line":1028},12,[814,1030,1031],{"class":824},"}\n",[605,1033,1034,1036,1037,1040,1041,1043,1044,1047,1048,1050],{},[614,1035,783],{}," checks batch size, JSON shape, ",[614,1038,1039],{},"event: 'run'",", tool allowlist, envelope types, ISO timestamp, and strips ",[614,1042,730],{}," keys you did not declare. Throws ",[614,1045,1046],{},"IngestValidationError"," on failure, so return ",[614,1049,700],{}," from your route.",[627,1052,1054],{"id":1053},"step-2-wire-your-route","Step 2: Wire your route",[605,1056,1057,1058,1061],{},"Same contract for any framework: read the raw body, validate, store, return ",[622,1059,1060],{},"204"," so the CLI clears its outbox:",[1063,1064,1065,1435,1692],"code-group",{},[804,1066,1069],{"className":806,"code":1067,"filename":1068,"language":809,"meta":810,"style":810},"import { defineEventHandler, readRawBody, setResponseStatus } from 'h3'\nimport { IngestValidationError } from '@evlog\u002Ftelemetry\u002Fingest'\nimport { parseTelemetryBody } from '~\u002Flib\u002Ftelemetry-ingest'\nimport { storeRunEvents } from '~\u002Flib\u002Ftelemetry-store'\n\nexport default defineEventHandler(async (event) => {\n  const raw = await readRawBody(event, 'utf8')\n  if (!raw) {\n    setResponseStatus(event, 400)\n    return { error: 'empty body' }\n  }\n\n  try {\n    const events = parseTelemetryBody(raw)\n    await storeRunEvents(events)\n    setResponseStatus(event, 204)\n    return null\n  } catch (err) {\n    setResponseStatus(event, err instanceof IngestValidationError ? 400 : 500)\n    return { error: 'invalid payload' }\n  }\n})\n","server\u002Fapi\u002Ftelemetry\u002Fingest.post.ts (Nitro)",[614,1070,1071,1101,1120,1139,1159,1163,1190,1221,1239,1256,1278,1283,1287,1295,1314,1329,1345,1353,1371,1403,1423,1428],{"__ignoreMap":810},[814,1072,1073,1075,1077,1080,1082,1085,1087,1090,1092,1094,1096,1099],{"class":816,"line":817},[814,1074,821],{"class":820},[814,1076,825],{"class":824},[814,1078,1079],{"class":828}," defineEventHandler",[814,1081,932],{"class":824},[814,1083,1084],{"class":828}," readRawBody",[814,1086,932],{"class":824},[814,1088,1089],{"class":828}," setResponseStatus",[814,1091,832],{"class":824},[814,1093,835],{"class":820},[814,1095,838],{"class":824},[814,1097,1098],{"class":841},"h3",[814,1100,845],{"class":824},[814,1102,1103,1105,1107,1110,1112,1114,1116,1118],{"class":816,"line":848},[814,1104,821],{"class":820},[814,1106,825],{"class":824},[814,1108,1109],{"class":828}," IngestValidationError",[814,1111,832],{"class":824},[814,1113,835],{"class":820},[814,1115,838],{"class":824},[814,1117,842],{"class":841},[814,1119,845],{"class":824},[814,1121,1122,1124,1126,1128,1130,1132,1134,1137],{"class":816,"line":855},[814,1123,821],{"class":820},[814,1125,825],{"class":824},[814,1127,987],{"class":828},[814,1129,832],{"class":824},[814,1131,835],{"class":820},[814,1133,838],{"class":824},[814,1135,1136],{"class":841},"~\u002Flib\u002Ftelemetry-ingest",[814,1138,845],{"class":824},[814,1140,1141,1143,1145,1148,1150,1152,1154,1157],{"class":816,"line":874},[814,1142,821],{"class":820},[814,1144,825],{"class":824},[814,1146,1147],{"class":828}," storeRunEvents",[814,1149,832],{"class":824},[814,1151,835],{"class":820},[814,1153,838],{"class":824},[814,1155,1156],{"class":841},"~\u002Flib\u002Ftelemetry-store",[814,1158,845],{"class":824},[814,1160,1161],{"class":816,"line":901},[814,1162,852],{"emptyLinePlaceholder":851},[814,1164,1165,1167,1170,1172,1174,1177,1180,1183,1185,1188],{"class":816,"line":911},[814,1166,858],{"class":820},[814,1168,1169],{"class":820}," default",[814,1171,1079],{"class":986},[814,1173,990],{"class":828},[814,1175,1176],{"class":861},"async",[814,1178,1179],{"class":824}," (",[814,1181,1182],{"class":993},"event",[814,1184,1003],{"class":824},[814,1186,1187],{"class":861}," =>",[814,1189,871],{"class":824},[814,1191,1192,1195,1198,1201,1204,1206,1208,1210,1212,1214,1217,1219],{"class":816,"line":955},[814,1193,1194],{"class":861},"  const",[814,1196,1197],{"class":828}," raw",[814,1199,1200],{"class":824}," =",[814,1202,1203],{"class":820}," await",[814,1205,1084],{"class":986},[814,1207,990],{"class":877},[814,1209,1182],{"class":828},[814,1211,932],{"class":824},[814,1213,838],{"class":824},[814,1215,1216],{"class":841},"utf8",[814,1218,887],{"class":824},[814,1220,1025],{"class":877},[814,1222,1223,1226,1228,1231,1233,1236],{"class":816,"line":961},[814,1224,1225],{"class":820},"  if",[814,1227,1179],{"class":877},[814,1229,1230],{"class":824},"!",[814,1232,994],{"class":828},[814,1234,1235],{"class":877},") ",[814,1237,1238],{"class":824},"{\n",[814,1240,1241,1244,1246,1248,1250,1254],{"class":816,"line":973},[814,1242,1243],{"class":986},"    setResponseStatus",[814,1245,990],{"class":877},[814,1247,1182],{"class":828},[814,1249,932],{"class":824},[814,1251,1253],{"class":1252},"sbssI"," 400",[814,1255,1025],{"class":877},[814,1257,1258,1261,1263,1266,1268,1270,1273,1275],{"class":816,"line":978},[814,1259,1260],{"class":820},"    return",[814,1262,825],{"class":824},[814,1264,1265],{"class":877}," error",[814,1267,881],{"class":824},[814,1269,838],{"class":824},[814,1271,1272],{"class":841},"empty body",[814,1274,887],{"class":824},[814,1276,1277],{"class":824}," }\n",[814,1279,1280],{"class":816,"line":1008},[814,1281,1282],{"class":824},"  }\n",[814,1284,1285],{"class":816,"line":1028},[814,1286,852],{"emptyLinePlaceholder":851},[814,1288,1290,1293],{"class":816,"line":1289},13,[814,1291,1292],{"class":820},"  try",[814,1294,871],{"class":824},[814,1296,1298,1301,1304,1306,1308,1310,1312],{"class":816,"line":1297},14,[814,1299,1300],{"class":861},"    const",[814,1302,1303],{"class":828}," events",[814,1305,1200],{"class":824},[814,1307,987],{"class":986},[814,1309,990],{"class":877},[814,1311,994],{"class":828},[814,1313,1025],{"class":877},[814,1315,1317,1320,1322,1324,1327],{"class":816,"line":1316},15,[814,1318,1319],{"class":820},"    await",[814,1321,1147],{"class":986},[814,1323,990],{"class":877},[814,1325,1326],{"class":828},"events",[814,1328,1025],{"class":877},[814,1330,1332,1334,1336,1338,1340,1343],{"class":816,"line":1331},16,[814,1333,1243],{"class":986},[814,1335,990],{"class":877},[814,1337,1182],{"class":828},[814,1339,932],{"class":824},[814,1341,1342],{"class":1252}," 204",[814,1344,1025],{"class":877},[814,1346,1348,1350],{"class":816,"line":1347},17,[814,1349,1260],{"class":820},[814,1351,1352],{"class":824}," null\n",[814,1354,1356,1359,1362,1364,1367,1369],{"class":816,"line":1355},18,[814,1357,1358],{"class":824},"  }",[814,1360,1361],{"class":820}," catch",[814,1363,1179],{"class":877},[814,1365,1366],{"class":828},"err",[814,1368,1235],{"class":877},[814,1370,1238],{"class":824},[814,1372,1374,1376,1378,1380,1382,1385,1388,1390,1393,1395,1398,1401],{"class":816,"line":1373},19,[814,1375,1243],{"class":986},[814,1377,990],{"class":877},[814,1379,1182],{"class":828},[814,1381,932],{"class":824},[814,1383,1384],{"class":828}," err",[814,1386,1387],{"class":824}," instanceof",[814,1389,1109],{"class":999},[814,1391,1392],{"class":824}," ?",[814,1394,1253],{"class":1252},[814,1396,1397],{"class":824}," :",[814,1399,1400],{"class":1252}," 500",[814,1402,1025],{"class":877},[814,1404,1406,1408,1410,1412,1414,1416,1419,1421],{"class":816,"line":1405},20,[814,1407,1260],{"class":820},[814,1409,825],{"class":824},[814,1411,1265],{"class":877},[814,1413,881],{"class":824},[814,1415,838],{"class":824},[814,1417,1418],{"class":841},"invalid payload",[814,1420,887],{"class":824},[814,1422,1277],{"class":824},[814,1424,1426],{"class":816,"line":1425},21,[814,1427,1282],{"class":824},[814,1429,1431,1433],{"class":816,"line":1430},22,[814,1432,964],{"class":824},[814,1434,1025],{"class":828},[804,1436,1439],{"className":806,"code":1437,"filename":1438,"language":809,"meta":810,"style":810},"import { IngestValidationError } from '@evlog\u002Ftelemetry\u002Fingest'\nimport { parseTelemetryBody } from '@\u002Flib\u002Ftelemetry-ingest'\nimport { storeRunEvents } from '@\u002Flib\u002Ftelemetry-store'\n\nexport async function POST(request: Request) {\n  const raw = await request.text()\n  try {\n    const events = parseTelemetryBody(raw)\n    await storeRunEvents(events)\n    return new Response(null, { status: 204 })\n  } catch (err) {\n    const status = err instanceof IngestValidationError ? 400 : 500\n    return Response.json({ error: 'invalid payload' }, { status })\n  }\n}\n","app\u002Fapi\u002Ftelemetry\u002Fingest\u002Froute.ts (Next.js App Router)",[614,1440,1441,1459,1478,1497,1501,1527,1548,1554,1570,1582,1610,1624,1647,1684,1688],{"__ignoreMap":810},[814,1442,1443,1445,1447,1449,1451,1453,1455,1457],{"class":816,"line":817},[814,1444,821],{"class":820},[814,1446,825],{"class":824},[814,1448,1109],{"class":828},[814,1450,832],{"class":824},[814,1452,835],{"class":820},[814,1454,838],{"class":824},[814,1456,842],{"class":841},[814,1458,845],{"class":824},[814,1460,1461,1463,1465,1467,1469,1471,1473,1476],{"class":816,"line":848},[814,1462,821],{"class":820},[814,1464,825],{"class":824},[814,1466,987],{"class":828},[814,1468,832],{"class":824},[814,1470,835],{"class":820},[814,1472,838],{"class":824},[814,1474,1475],{"class":841},"@\u002Flib\u002Ftelemetry-ingest",[814,1477,845],{"class":824},[814,1479,1480,1482,1484,1486,1488,1490,1492,1495],{"class":816,"line":855},[814,1481,821],{"class":820},[814,1483,825],{"class":824},[814,1485,1147],{"class":828},[814,1487,832],{"class":824},[814,1489,835],{"class":820},[814,1491,838],{"class":824},[814,1493,1494],{"class":841},"@\u002Flib\u002Ftelemetry-store",[814,1496,845],{"class":824},[814,1498,1499],{"class":816,"line":874},[814,1500,852],{"emptyLinePlaceholder":851},[814,1502,1503,1505,1508,1510,1513,1515,1518,1520,1523,1525],{"class":816,"line":901},[814,1504,858],{"class":820},[814,1506,1507],{"class":861}," async",[814,1509,983],{"class":861},[814,1511,1512],{"class":986}," POST",[814,1514,990],{"class":824},[814,1516,1517],{"class":993},"request",[814,1519,881],{"class":824},[814,1521,1522],{"class":999}," Request",[814,1524,1003],{"class":824},[814,1526,871],{"class":824},[814,1528,1529,1531,1533,1535,1537,1540,1542,1545],{"class":816,"line":911},[814,1530,1194],{"class":861},[814,1532,1197],{"class":828},[814,1534,1200],{"class":824},[814,1536,1203],{"class":820},[814,1538,1539],{"class":828}," request",[814,1541,617],{"class":824},[814,1543,1544],{"class":986},"text",[814,1546,1547],{"class":877},"()\n",[814,1549,1550,1552],{"class":816,"line":955},[814,1551,1292],{"class":820},[814,1553,871],{"class":824},[814,1555,1556,1558,1560,1562,1564,1566,1568],{"class":816,"line":961},[814,1557,1300],{"class":861},[814,1559,1303],{"class":828},[814,1561,1200],{"class":824},[814,1563,987],{"class":986},[814,1565,990],{"class":877},[814,1567,994],{"class":828},[814,1569,1025],{"class":877},[814,1571,1572,1574,1576,1578,1580],{"class":816,"line":973},[814,1573,1319],{"class":820},[814,1575,1147],{"class":986},[814,1577,990],{"class":877},[814,1579,1326],{"class":828},[814,1581,1025],{"class":877},[814,1583,1584,1586,1589,1592,1594,1597,1599,1602,1604,1606,1608],{"class":816,"line":978},[814,1585,1260],{"class":820},[814,1587,1588],{"class":824}," new",[814,1590,1591],{"class":986}," Response",[814,1593,990],{"class":877},[814,1595,1596],{"class":824},"null,",[814,1598,825],{"class":824},[814,1600,1601],{"class":877}," status",[814,1603,881],{"class":824},[814,1605,1342],{"class":1252},[814,1607,832],{"class":824},[814,1609,1025],{"class":877},[814,1611,1612,1614,1616,1618,1620,1622],{"class":816,"line":1008},[814,1613,1358],{"class":824},[814,1615,1361],{"class":820},[814,1617,1179],{"class":877},[814,1619,1366],{"class":828},[814,1621,1235],{"class":877},[814,1623,1238],{"class":824},[814,1625,1626,1628,1630,1632,1634,1636,1638,1640,1642,1644],{"class":816,"line":1028},[814,1627,1300],{"class":861},[814,1629,1601],{"class":828},[814,1631,1200],{"class":824},[814,1633,1384],{"class":828},[814,1635,1387],{"class":824},[814,1637,1109],{"class":999},[814,1639,1392],{"class":824},[814,1641,1253],{"class":1252},[814,1643,1397],{"class":824},[814,1645,1646],{"class":1252}," 500\n",[814,1648,1649,1651,1653,1655,1658,1660,1663,1665,1667,1669,1671,1673,1676,1678,1680,1682],{"class":816,"line":1289},[814,1650,1260],{"class":820},[814,1652,1591],{"class":828},[814,1654,617],{"class":824},[814,1656,1657],{"class":986},"json",[814,1659,990],{"class":877},[814,1661,1662],{"class":824},"{",[814,1664,1265],{"class":877},[814,1666,881],{"class":824},[814,1668,838],{"class":824},[814,1670,1418],{"class":841},[814,1672,887],{"class":824},[814,1674,1675],{"class":824}," },",[814,1677,825],{"class":824},[814,1679,1601],{"class":828},[814,1681,832],{"class":824},[814,1683,1025],{"class":877},[814,1685,1686],{"class":816,"line":1297},[814,1687,1282],{"class":824},[814,1689,1690],{"class":816,"line":1316},[814,1691,1031],{"class":824},[804,1693,1696],{"className":806,"code":1694,"filename":1695,"language":809,"meta":810,"style":810},"import { Hono } from 'hono'\nimport { IngestValidationError } from '@evlog\u002Ftelemetry\u002Fingest'\nimport { parseTelemetryBody } from '..\u002Flib\u002Ftelemetry-ingest'\nimport { storeRunEvents } from '..\u002Flib\u002Ftelemetry-store'\n\nconst app = new Hono()\n\napp.post('\u002Fapi\u002Ftelemetry\u002Fingest', async (c) => {\n  const raw = await c.req.text()\n  try {\n    const events = parseTelemetryBody(raw)\n    await storeRunEvents(events)\n    return c.body(null, 204)\n  } catch (err) {\n    const status = err instanceof IngestValidationError ? 400 : 500\n    return c.json({ error: 'invalid payload' }, status)\n  }\n})\n\nexport default app\n","src\u002Froutes\u002Ftelemetry.ts (Hono)",[614,1697,1698,1718,1736,1755,1774,1778,1794,1798,1832,1856,1862,1878,1890,1909,1923,1945,1975,1979,1985,1989],{"__ignoreMap":810},[814,1699,1700,1702,1704,1707,1709,1711,1713,1716],{"class":816,"line":817},[814,1701,821],{"class":820},[814,1703,825],{"class":824},[814,1705,1706],{"class":828}," Hono",[814,1708,832],{"class":824},[814,1710,835],{"class":820},[814,1712,838],{"class":824},[814,1714,1715],{"class":841},"hono",[814,1717,845],{"class":824},[814,1719,1720,1722,1724,1726,1728,1730,1732,1734],{"class":816,"line":848},[814,1721,821],{"class":820},[814,1723,825],{"class":824},[814,1725,1109],{"class":828},[814,1727,832],{"class":824},[814,1729,835],{"class":820},[814,1731,838],{"class":824},[814,1733,842],{"class":841},[814,1735,845],{"class":824},[814,1737,1738,1740,1742,1744,1746,1748,1750,1753],{"class":816,"line":855},[814,1739,821],{"class":820},[814,1741,825],{"class":824},[814,1743,987],{"class":828},[814,1745,832],{"class":824},[814,1747,835],{"class":820},[814,1749,838],{"class":824},[814,1751,1752],{"class":841},"..\u002Flib\u002Ftelemetry-ingest",[814,1754,845],{"class":824},[814,1756,1757,1759,1761,1763,1765,1767,1769,1772],{"class":816,"line":874},[814,1758,821],{"class":820},[814,1760,825],{"class":824},[814,1762,1147],{"class":828},[814,1764,832],{"class":824},[814,1766,835],{"class":820},[814,1768,838],{"class":824},[814,1770,1771],{"class":841},"..\u002Flib\u002Ftelemetry-store",[814,1773,845],{"class":824},[814,1775,1776],{"class":816,"line":901},[814,1777,852],{"emptyLinePlaceholder":851},[814,1779,1780,1783,1786,1788,1790,1792],{"class":816,"line":911},[814,1781,1782],{"class":861},"const",[814,1784,1785],{"class":828}," app ",[814,1787,868],{"class":824},[814,1789,1588],{"class":824},[814,1791,1706],{"class":986},[814,1793,1547],{"class":828},[814,1795,1796],{"class":816,"line":955},[814,1797,852],{"emptyLinePlaceholder":851},[814,1799,1800,1803,1805,1808,1810,1812,1815,1817,1819,1821,1823,1826,1828,1830],{"class":816,"line":961},[814,1801,1802],{"class":828},"app",[814,1804,617],{"class":824},[814,1806,1807],{"class":986},"post",[814,1809,990],{"class":828},[814,1811,887],{"class":824},[814,1813,1814],{"class":841},"\u002Fapi\u002Ftelemetry\u002Fingest",[814,1816,887],{"class":824},[814,1818,932],{"class":824},[814,1820,1507],{"class":861},[814,1822,1179],{"class":824},[814,1824,1825],{"class":993},"c",[814,1827,1003],{"class":824},[814,1829,1187],{"class":861},[814,1831,871],{"class":824},[814,1833,1834,1836,1838,1840,1842,1845,1847,1850,1852,1854],{"class":816,"line":973},[814,1835,1194],{"class":861},[814,1837,1197],{"class":828},[814,1839,1200],{"class":824},[814,1841,1203],{"class":820},[814,1843,1844],{"class":828}," c",[814,1846,617],{"class":824},[814,1848,1849],{"class":828},"req",[814,1851,617],{"class":824},[814,1853,1544],{"class":986},[814,1855,1547],{"class":877},[814,1857,1858,1860],{"class":816,"line":978},[814,1859,1292],{"class":820},[814,1861,871],{"class":824},[814,1863,1864,1866,1868,1870,1872,1874,1876],{"class":816,"line":1008},[814,1865,1300],{"class":861},[814,1867,1303],{"class":828},[814,1869,1200],{"class":824},[814,1871,987],{"class":986},[814,1873,990],{"class":877},[814,1875,994],{"class":828},[814,1877,1025],{"class":877},[814,1879,1880,1882,1884,1886,1888],{"class":816,"line":1028},[814,1881,1319],{"class":820},[814,1883,1147],{"class":986},[814,1885,990],{"class":877},[814,1887,1326],{"class":828},[814,1889,1025],{"class":877},[814,1891,1892,1894,1896,1898,1901,1903,1905,1907],{"class":816,"line":1289},[814,1893,1260],{"class":820},[814,1895,1844],{"class":828},[814,1897,617],{"class":824},[814,1899,1900],{"class":986},"body",[814,1902,990],{"class":877},[814,1904,1596],{"class":824},[814,1906,1342],{"class":1252},[814,1908,1025],{"class":877},[814,1910,1911,1913,1915,1917,1919,1921],{"class":816,"line":1297},[814,1912,1358],{"class":824},[814,1914,1361],{"class":820},[814,1916,1179],{"class":877},[814,1918,1366],{"class":828},[814,1920,1235],{"class":877},[814,1922,1238],{"class":824},[814,1924,1925,1927,1929,1931,1933,1935,1937,1939,1941,1943],{"class":816,"line":1316},[814,1926,1300],{"class":861},[814,1928,1601],{"class":828},[814,1930,1200],{"class":824},[814,1932,1384],{"class":828},[814,1934,1387],{"class":824},[814,1936,1109],{"class":999},[814,1938,1392],{"class":824},[814,1940,1253],{"class":1252},[814,1942,1397],{"class":824},[814,1944,1646],{"class":1252},[814,1946,1947,1949,1951,1953,1955,1957,1959,1961,1963,1965,1967,1969,1971,1973],{"class":816,"line":1331},[814,1948,1260],{"class":820},[814,1950,1844],{"class":828},[814,1952,617],{"class":824},[814,1954,1657],{"class":986},[814,1956,990],{"class":877},[814,1958,1662],{"class":824},[814,1960,1265],{"class":877},[814,1962,881],{"class":824},[814,1964,838],{"class":824},[814,1966,1418],{"class":841},[814,1968,887],{"class":824},[814,1970,1675],{"class":824},[814,1972,1601],{"class":828},[814,1974,1025],{"class":877},[814,1976,1977],{"class":816,"line":1347},[814,1978,1282],{"class":824},[814,1980,1981,1983],{"class":816,"line":1355},[814,1982,964],{"class":824},[814,1984,1025],{"class":828},[814,1986,1987],{"class":816,"line":1373},[814,1988,852],{"emptyLinePlaceholder":851},[814,1990,1991,1993,1995],{"class":816,"line":1405},[814,1992,858],{"class":820},[814,1994,1169],{"class":820},[814,1996,1997],{"class":828}," app\n",[627,1999,2001],{"id":2000},"step-3-store-idempotently","Step 3: Store idempotently",[605,2003,2004,2005,2007],{},"Retries and backlog drains can POST the same ",[614,2006,748],{}," twice. Upsert (or skip) so metrics stay correct:",[804,2009,2012],{"className":806,"code":2010,"filename":2011,"language":809,"meta":810,"style":810},"import type { RunEvent } from '@evlog\u002Ftelemetry'\n\nexport async function storeRunEvents(events: RunEvent[]): Promise\u003Cvoid> {\n  for (const run of events) {\n    await db.telemetryRun.upsert({\n      where: { idempotencyKey: run.idempotencyKey },\n      create: {\n        tool: run.tool.name,\n        version: run.tool.version,\n        command: run.command,\n        outcome: run.outcome,\n        durationMs: run.durationMs,\n        custom: run.custom,\n        recordedAt: run.timestamp,\n      },\n      update: {},\n    })\n  }\n\n  \u002F\u002F Or forward into evlog's drain pipeline for Axiom \u002F Datadog \u002F OTLP:\n  \u002F\u002F await ingestWideEvents(events.map(run => ({ source: 'telemetry', ...run })))\n}\n","lib\u002Ftelemetry-store.ts",[614,2013,2014,2036,2040,2078,2099,2120,2143,2152,2172,2192,2208,2224,2240,2255,2271,2276,2286,2293,2297,2301,2307,2312],{"__ignoreMap":810},[814,2015,2016,2018,2021,2023,2026,2028,2030,2032,2034],{"class":816,"line":817},[814,2017,821],{"class":820},[814,2019,2020],{"class":820}," type",[814,2022,825],{"class":824},[814,2024,2025],{"class":828}," RunEvent",[814,2027,832],{"class":824},[814,2029,835],{"class":820},[814,2031,838],{"class":824},[814,2033,777],{"class":841},[814,2035,845],{"class":824},[814,2037,2038],{"class":816,"line":848},[814,2039,852],{"emptyLinePlaceholder":851},[814,2041,2042,2044,2046,2048,2050,2052,2054,2056,2058,2061,2064,2067,2070,2073,2076],{"class":816,"line":855},[814,2043,858],{"class":820},[814,2045,1507],{"class":861},[814,2047,983],{"class":861},[814,2049,1147],{"class":986},[814,2051,990],{"class":824},[814,2053,1326],{"class":993},[814,2055,881],{"class":824},[814,2057,2025],{"class":999},[814,2059,2060],{"class":828},"[]",[814,2062,2063],{"class":824},"):",[814,2065,2066],{"class":999}," Promise",[814,2068,2069],{"class":824},"\u003C",[814,2071,2072],{"class":999},"void",[814,2074,2075],{"class":824},">",[814,2077,871],{"class":824},[814,2079,2080,2083,2085,2087,2090,2093,2095,2097],{"class":816,"line":874},[814,2081,2082],{"class":820},"  for",[814,2084,1179],{"class":877},[814,2086,1782],{"class":861},[814,2088,2089],{"class":828}," run",[814,2091,2092],{"class":824}," of",[814,2094,1303],{"class":828},[814,2096,1235],{"class":877},[814,2098,1238],{"class":824},[814,2100,2101,2103,2106,2108,2111,2113,2116,2118],{"class":816,"line":901},[814,2102,1319],{"class":820},[814,2104,2105],{"class":828}," db",[814,2107,617],{"class":824},[814,2109,2110],{"class":828},"telemetryRun",[814,2112,617],{"class":824},[814,2114,2115],{"class":986},"upsert",[814,2117,990],{"class":877},[814,2119,1238],{"class":824},[814,2121,2122,2125,2127,2129,2132,2134,2136,2138,2140],{"class":816,"line":911},[814,2123,2124],{"class":877},"      where",[814,2126,881],{"class":824},[814,2128,825],{"class":824},[814,2130,2131],{"class":877}," idempotencyKey",[814,2133,881],{"class":824},[814,2135,2089],{"class":828},[814,2137,617],{"class":824},[814,2139,748],{"class":828},[814,2141,2142],{"class":824}," },\n",[814,2144,2145,2148,2150],{"class":816,"line":955},[814,2146,2147],{"class":877},"      create",[814,2149,881],{"class":824},[814,2151,871],{"class":824},[814,2153,2154,2157,2159,2161,2163,2166,2168,2170],{"class":816,"line":961},[814,2155,2156],{"class":877},"        tool",[814,2158,881],{"class":824},[814,2160,2089],{"class":828},[814,2162,617],{"class":824},[814,2164,2165],{"class":828},"tool",[814,2167,617],{"class":824},[814,2169,796],{"class":828},[814,2171,898],{"class":824},[814,2173,2174,2177,2179,2181,2183,2185,2187,2190],{"class":816,"line":973},[814,2175,2176],{"class":877},"        version",[814,2178,881],{"class":824},[814,2180,2089],{"class":828},[814,2182,617],{"class":824},[814,2184,2165],{"class":828},[814,2186,617],{"class":824},[814,2188,2189],{"class":828},"version",[814,2191,898],{"class":824},[814,2193,2194,2197,2199,2201,2203,2206],{"class":816,"line":978},[814,2195,2196],{"class":877},"        command",[814,2198,881],{"class":824},[814,2200,2089],{"class":828},[814,2202,617],{"class":824},[814,2204,2205],{"class":828},"command",[814,2207,898],{"class":824},[814,2209,2210,2213,2215,2217,2219,2222],{"class":816,"line":1008},[814,2211,2212],{"class":877},"        outcome",[814,2214,881],{"class":824},[814,2216,2089],{"class":828},[814,2218,617],{"class":824},[814,2220,2221],{"class":828},"outcome",[814,2223,898],{"class":824},[814,2225,2226,2229,2231,2233,2235,2238],{"class":816,"line":1028},[814,2227,2228],{"class":877},"        durationMs",[814,2230,881],{"class":824},[814,2232,2089],{"class":828},[814,2234,617],{"class":824},[814,2236,2237],{"class":828},"durationMs",[814,2239,898],{"class":824},[814,2241,2242,2245,2247,2249,2251,2253],{"class":816,"line":1289},[814,2243,2244],{"class":877},"        custom",[814,2246,881],{"class":824},[814,2248,2089],{"class":828},[814,2250,617],{"class":824},[814,2252,730],{"class":828},[814,2254,898],{"class":824},[814,2256,2257,2260,2262,2264,2266,2269],{"class":816,"line":1297},[814,2258,2259],{"class":877},"        recordedAt",[814,2261,881],{"class":824},[814,2263,2089],{"class":828},[814,2265,617],{"class":824},[814,2267,2268],{"class":828},"timestamp",[814,2270,898],{"class":824},[814,2272,2273],{"class":816,"line":1316},[814,2274,2275],{"class":824},"      },\n",[814,2277,2278,2281,2283],{"class":816,"line":1331},[814,2279,2280],{"class":877},"      update",[814,2282,881],{"class":824},[814,2284,2285],{"class":824}," {},\n",[814,2287,2288,2291],{"class":816,"line":1347},[814,2289,2290],{"class":824},"    }",[814,2292,1025],{"class":877},[814,2294,2295],{"class":816,"line":1355},[814,2296,1282],{"class":824},[814,2298,2299],{"class":816,"line":1373},[814,2300,852],{"emptyLinePlaceholder":851},[814,2302,2303],{"class":816,"line":1405},[814,2304,2306],{"class":2305},"sHwdD","  \u002F\u002F Or forward into evlog's drain pipeline for Axiom \u002F Datadog \u002F OTLP:\n",[814,2308,2309],{"class":816,"line":1425},[814,2310,2311],{"class":2305},"  \u002F\u002F await ingestWideEvents(events.map(run => ({ source: 'telemetry', ...run })))\n",[814,2313,2314],{"class":816,"line":1430},[814,2315,1031],{"class":824},[627,2317,2319],{"id":2318},"step-4-rate-limit-at-the-edge","Step 4: Rate-limit at the edge",[605,2321,2322,2324,2325,2328],{},[614,2323,783],{}," is your last line of schema defense, not your only one. Add rate limits where traffic enters (CDN, API gateway, or middleware), especially per IP. Optional: also bucket by ",[614,2326,2327],{},"machineId"," hash inside the handler if you need tighter control.",[605,2330,2331,2332,2334,2335,2338],{},"Non-2xx responses keep events in the user's outbox; they will retry on the next CLI invocation. Return ",[622,2333,1060],{}," (or any ",[614,2336,2337],{},"2xx",") only when storage succeeded.",[2340,2341,2342],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":810,"searchDepth":848,"depth":848,"links":2344},[2345,2346,2348,2349,2350],{"id":629,"depth":848,"text":630},{"id":787,"depth":848,"text":2347},"Step 1: Validate with parseIngestBody()",{"id":1053,"depth":848,"text":1054},{"id":2000,"depth":848,"text":2001},{"id":2318,"depth":848,"text":2319},"Build the ingestion endpoint for @evlog\u002Ftelemetry: the threat model, parseIngestBody validation, framework routes, storage, and rate limiting.","md",[2354,2357,2358],{"label":41,"icon":44,"to":423,"color":2355,"variant":2356},"neutral","subtle",{"label":426,"icon":429,"to":427,"color":2355,"variant":2356},{"label":436,"icon":84,"to":437,"color":2355,"variant":2356},{},{"title":431,"icon":434},{"title":600,"description":2351},"rFp9uwlFbw94P_AK3Grez6dlVtWQc2B56C4Go2gQXgc",[2364,2366],{"title":426,"path":427,"stem":428,"description":2365,"icon":429,"children":-1},"Wire @evlog\u002Ftelemetry into citty CLIs, standalone scripts, and GitHub Actions — delivery config, automatic flag capture, telemetry.set(), and debug mode.",{"title":436,"path":437,"stem":438,"description":2367,"icon":84,"children":-1},"RunEvent envelope, extending the schema with collect and custom fields, privacy rules, TELEMETRY.md disclosure, and consent \u002F outbox reliability.",1789076909476]