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