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