[{"data":1,"prerenderedAt":2295},["ShallowReactive",2],{"navigation_docs":3,"-use-cases-telemetry-ingest":525,"-use-cases-telemetry-ingest-surround":2290},[4,30,80,128,308,440,495],{"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,464,468,472,476,480,485,490],{"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":463},"FS reader","\u002Fextend\u002Ffs-reader","6.extend\u002F2.fs-reader","i-lucide-folder-search",{"title":406,"path":465,"stem":466,"icon":467},"\u002Fextend\u002Fconsumer-recipes","6.extend\u002F3.consumer-recipes","i-lucide-chef-hat",{"title":469,"path":470,"stem":471,"icon":448},"Plugins","\u002Fextend\u002Fplugins","6.extend\u002F4.plugins",{"title":473,"path":474,"stem":475,"icon":434},"Custom enrichers","\u002Fextend\u002Fcustom-enrichers","6.extend\u002F5.custom-enrichers",{"title":477,"path":478,"stem":479,"icon":64},"Tail sampling","\u002Fextend\u002Ftail-sampling","6.extend\u002F6.tail-sampling",{"title":481,"path":482,"stem":483,"icon":484},"Identity headers","\u002Fextend\u002Fidentity-headers","6.extend\u002F7.identity-headers","i-lucide-fingerprint",{"title":486,"path":487,"stem":488,"icon":489},"Custom drains","\u002Fextend\u002Fcustom-drains","6.extend\u002F8.custom-drains","i-lucide-share-2",{"title":491,"path":492,"stem":493,"icon":494},"Drain pipeline","\u002Fextend\u002Fdrain-pipeline","6.extend\u002F9.drain-pipeline","i-lucide-workflow",{"title":427,"path":496,"stem":497,"children":498,"page":29},"\u002Freference","7.reference",[499,504,507,512,516,521],{"title":500,"path":501,"stem":502,"icon":503},"Configuration","\u002Freference\u002Fconfiguration","7.reference\u002F1.configuration","i-lucide-settings",{"title":374,"path":505,"stem":506,"icon":107},"\u002Freference\u002Fperformance","7.reference\u002F2.performance",{"title":508,"path":509,"stem":510,"icon":511},"Vite Plugin","\u002Freference\u002Fvite-plugin","7.reference\u002F3.vite-plugin","i-custom-vite",{"title":513,"path":514,"stem":515,"icon":379},"Best Practices","\u002Freference\u002Fbest-practices","7.reference\u002F4.best-practices",{"title":517,"path":518,"stem":519,"icon":520},"vs Other Loggers","\u002Freference\u002Fvs-other-loggers","7.reference\u002F5.vs-other-loggers","i-lucide-scale",{"title":522,"path":523,"stem":524,"icon":434},"Agent Skills","\u002Freference\u002Fagent-skills","7.reference\u002F6.agent-skills",{"id":526,"title":527,"body":528,"description":2278,"extension":2279,"links":2280,"meta":2286,"navigation":2287,"path":423,"seo":2288,"stem":424,"__hash__":2289},"docs\u002F5.use-cases\u002F4.telemetry\u002F03.ingest.md","Telemetry Ingest",{"type":529,"value":530,"toc":2270},"minimark",[531,545,553,558,565,570,581,586,597,692,698,712,718,730,959,978,982,989,1925,1929,1935,2243,2247,2256,2266],[532,533,534,535,539,540,544],"p",{},"You ship a CLI. Users run it on their machines. You want to know ",[536,537,538],"em",{},"which commands run, how often they fail, and which versions are out there"," — without building a separate analytics SDK or reading raw ",[541,542,543],"code",{},"argv",".",[532,546,547,548,552],{},"That means a ",[549,550,551],"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.",[554,555,557],"h2",{"id":556},"the-security-question-read-this-first","The security question (read this first)",[559,560,561,564],"tip",{},[549,562,563],{},"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.",[532,566,567],{},[549,568,569],{},"\"I put the ingest URL in my CLI — isn't that a secret?\"",[532,571,572,573,576,577,580],{},"No. The URL lives in your npm package or binary. Anyone can read it with ",[541,574,575],{},"strings",", by installing the package, or by watching network traffic. The same applies to an API key or ",[541,578,579],{},"Authorization"," header baked into the CLI — extractable, not a real barrier.",[532,582,583],{},[549,584,585],{},"\"So can't anyone send fake events?\"",[532,587,588,589,592,593,596],{},"In theory, yes — someone could ",[541,590,591],{},"curl"," your endpoint with forged JSON. That is why you treat the endpoint as ",[549,594,595],{},"semi-public"," (like a browser analytics key) and defend on the server:",[598,599,600,613],"table",{},[601,602,603],"thead",{},[604,605,606,610],"tr",{},[607,608,609],"th",{},"What you might worry about",[607,611,612],{},"Practical answer",[614,615,616,628,639,650,665,677],"tbody",{},[604,617,618,622],{},[619,620,621],"td",{},"Random internet bots",[619,623,624,625],{},"Validate the payload shape; reject garbage with ",[541,626,627],{},"400",[604,629,630,636],{},[619,631,632,633,635],{},"Someone spamming fake ",[541,634,114],{}," runs",[619,637,638],{},"Rate-limit by IP; data is low-value counters, not money",[604,640,641,647],{},[619,642,643,644],{},"Forged ",[541,645,646],{},"tool.name",[619,648,649],{},"Allowlist only your published tool names server-side",[604,651,652,658],{},[619,653,654,655],{},"Unexpected fields in ",[541,656,657],{},"custom",[619,659,660,661,664],{},"Filter to the keys you declared — same list as ",[541,662,663],{},"collect"," on the CLI",[604,666,667,670],{},[619,668,669],{},"Double-counting on retry",[619,671,672,673,676],{},"Dedupe on ",[541,674,675],{},"idempotencyKey"," when storing",[604,678,679,682],{},[619,680,681],{},"Leaking user secrets",[619,683,684,685,687,688,691],{},"CLI never reads raw ",[541,686,543],{},"; you only allowlisted flags and ",[541,689,690],{},"telemetry.set()"," counters",[532,693,694,697],{},[549,695,696],{},"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.",[699,700,702,705,706,711],"callout",{"color":701,"icon":379},"info",[541,703,704],{},"@evlog\u002Ftelemetry"," ships ",[549,707,708],{},[541,709,710],{},"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.",[554,713,715,716],{"id":714},"step-1-validate-with-parseingestbody","Step 1 — Validate with ",[541,717,710],{},[532,719,720,721,724,725,727,728,544],{},"Mirror the CLI config on the server: same ",[541,722,723],{},"name",", same custom keys you allow via ",[541,726,663],{}," \u002F ",[541,729,690],{},[731,732,738],"pre",{"className":733,"code":734,"filename":735,"language":736,"meta":737,"style":737},"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","",[541,739,740,773,780,799,826,836,880,886,898,903,933,953],{"__ignoreMap":737},[741,742,745,749,753,757,760,763,766,770],"span",{"class":743,"line":744},"line",1,[741,746,748],{"class":747},"s7zQu","import",[741,750,752],{"class":751},"sMK4o"," {",[741,754,756],{"class":755},"sTEyZ"," parseIngestBody",[741,758,759],{"class":751}," }",[741,761,762],{"class":747}," from",[741,764,765],{"class":751}," '",[741,767,769],{"class":768},"sfazB","@evlog\u002Ftelemetry\u002Fingest",[741,771,772],{"class":751},"'\n",[741,774,776],{"class":743,"line":775},2,[741,777,779],{"emptyLinePlaceholder":778},true,"\n",[741,781,783,786,790,793,796],{"class":743,"line":782},3,[741,784,785],{"class":747},"export",[741,787,789],{"class":788},"spNyl"," const",[741,791,792],{"class":755}," ingestOptions ",[741,794,795],{"class":751},"=",[741,797,798],{"class":751}," {\n",[741,800,802,806,809,812,815,818,820,823],{"class":743,"line":801},4,[741,803,805],{"class":804},"swJcz","  allowedTools",[741,807,808],{"class":751},":",[741,810,811],{"class":755}," [",[741,813,814],{"class":751},"'",[741,816,817],{"class":768},"my-tool",[741,819,814],{"class":751},[741,821,822],{"class":755},"]",[741,824,825],{"class":751},",\n",[741,827,829,832,834],{"class":743,"line":828},5,[741,830,831],{"class":804},"  allowedCustomKeys",[741,833,808],{"class":751},[741,835,798],{"class":751},[741,837,839,842,844,846,848,850,852,855,857,860,862,865,867,869,871,874,876,878],{"class":743,"line":838},6,[741,840,841],{"class":751},"    '",[741,843,817],{"class":804},[741,845,814],{"class":751},[741,847,808],{"class":751},[741,849,811],{"class":755},[741,851,814],{"class":751},[741,853,854],{"class":768},"checksFailed",[741,856,814],{"class":751},[741,858,859],{"class":751},",",[741,861,765],{"class":751},[741,863,864],{"class":768},"checksWarn",[741,866,814],{"class":751},[741,868,859],{"class":751},[741,870,765],{"class":751},[741,872,873],{"class":768},"itemsSynced",[741,875,814],{"class":751},[741,877,822],{"class":755},[741,879,825],{"class":751},[741,881,883],{"class":743,"line":882},7,[741,884,885],{"class":751},"  },\n",[741,887,889,892,895],{"class":743,"line":888},8,[741,890,891],{"class":751},"}",[741,893,894],{"class":747}," as",[741,896,897],{"class":788}," const\n",[741,899,901],{"class":743,"line":900},9,[741,902,779],{"emptyLinePlaceholder":778},[741,904,906,908,911,915,918,922,924,928,931],{"class":743,"line":905},10,[741,907,785],{"class":747},[741,909,910],{"class":788}," function",[741,912,914],{"class":913},"s2Zo4"," parseTelemetryBody",[741,916,917],{"class":751},"(",[741,919,921],{"class":920},"sHdIc","raw",[741,923,808],{"class":751},[741,925,927],{"class":926},"sBMFI"," string",[741,929,930],{"class":751},")",[741,932,798],{"class":751},[741,934,936,939,941,943,945,947,950],{"class":743,"line":935},11,[741,937,938],{"class":747},"  return",[741,940,756],{"class":913},[741,942,917],{"class":804},[741,944,921],{"class":755},[741,946,859],{"class":751},[741,948,949],{"class":755}," ingestOptions",[741,951,952],{"class":804},")\n",[741,954,956],{"class":743,"line":955},12,[741,957,958],{"class":751},"}\n",[532,960,961,963,964,967,968,970,971,974,975,977],{},[541,962,710],{}," checks batch size, JSON shape, ",[541,965,966],{},"event: 'run'",", tool allowlist, envelope types, ISO timestamp, and strips ",[541,969,657],{}," keys you did not declare. Throws ",[541,972,973],{},"IngestValidationError"," on failure — return ",[541,976,627],{}," from your route.",[554,979,981],{"id":980},"step-2-wire-your-route","Step 2 — Wire your route",[532,983,984,985,988],{},"Same contract for any framework — read the raw body, validate, store, return ",[549,986,987],{},"204"," so the CLI clears its outbox:",[990,991,992,1362,1619],"code-group",{},[731,993,996],{"className":733,"code":994,"filename":995,"language":736,"meta":737,"style":737},"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)",[541,997,998,1028,1047,1066,1086,1090,1117,1148,1166,1183,1205,1210,1214,1222,1241,1256,1272,1280,1298,1330,1350,1355],{"__ignoreMap":737},[741,999,1000,1002,1004,1007,1009,1012,1014,1017,1019,1021,1023,1026],{"class":743,"line":744},[741,1001,748],{"class":747},[741,1003,752],{"class":751},[741,1005,1006],{"class":755}," defineEventHandler",[741,1008,859],{"class":751},[741,1010,1011],{"class":755}," readRawBody",[741,1013,859],{"class":751},[741,1015,1016],{"class":755}," setResponseStatus",[741,1018,759],{"class":751},[741,1020,762],{"class":747},[741,1022,765],{"class":751},[741,1024,1025],{"class":768},"h3",[741,1027,772],{"class":751},[741,1029,1030,1032,1034,1037,1039,1041,1043,1045],{"class":743,"line":775},[741,1031,748],{"class":747},[741,1033,752],{"class":751},[741,1035,1036],{"class":755}," IngestValidationError",[741,1038,759],{"class":751},[741,1040,762],{"class":747},[741,1042,765],{"class":751},[741,1044,769],{"class":768},[741,1046,772],{"class":751},[741,1048,1049,1051,1053,1055,1057,1059,1061,1064],{"class":743,"line":782},[741,1050,748],{"class":747},[741,1052,752],{"class":751},[741,1054,914],{"class":755},[741,1056,759],{"class":751},[741,1058,762],{"class":747},[741,1060,765],{"class":751},[741,1062,1063],{"class":768},"~\u002Flib\u002Ftelemetry-ingest",[741,1065,772],{"class":751},[741,1067,1068,1070,1072,1075,1077,1079,1081,1084],{"class":743,"line":801},[741,1069,748],{"class":747},[741,1071,752],{"class":751},[741,1073,1074],{"class":755}," storeRunEvents",[741,1076,759],{"class":751},[741,1078,762],{"class":747},[741,1080,765],{"class":751},[741,1082,1083],{"class":768},"~\u002Flib\u002Ftelemetry-store",[741,1085,772],{"class":751},[741,1087,1088],{"class":743,"line":828},[741,1089,779],{"emptyLinePlaceholder":778},[741,1091,1092,1094,1097,1099,1101,1104,1107,1110,1112,1115],{"class":743,"line":838},[741,1093,785],{"class":747},[741,1095,1096],{"class":747}," default",[741,1098,1006],{"class":913},[741,1100,917],{"class":755},[741,1102,1103],{"class":788},"async",[741,1105,1106],{"class":751}," (",[741,1108,1109],{"class":920},"event",[741,1111,930],{"class":751},[741,1113,1114],{"class":788}," =>",[741,1116,798],{"class":751},[741,1118,1119,1122,1125,1128,1131,1133,1135,1137,1139,1141,1144,1146],{"class":743,"line":882},[741,1120,1121],{"class":788},"  const",[741,1123,1124],{"class":755}," raw",[741,1126,1127],{"class":751}," =",[741,1129,1130],{"class":747}," await",[741,1132,1011],{"class":913},[741,1134,917],{"class":804},[741,1136,1109],{"class":755},[741,1138,859],{"class":751},[741,1140,765],{"class":751},[741,1142,1143],{"class":768},"utf8",[741,1145,814],{"class":751},[741,1147,952],{"class":804},[741,1149,1150,1153,1155,1158,1160,1163],{"class":743,"line":888},[741,1151,1152],{"class":747},"  if",[741,1154,1106],{"class":804},[741,1156,1157],{"class":751},"!",[741,1159,921],{"class":755},[741,1161,1162],{"class":804},") ",[741,1164,1165],{"class":751},"{\n",[741,1167,1168,1171,1173,1175,1177,1181],{"class":743,"line":900},[741,1169,1170],{"class":913},"    setResponseStatus",[741,1172,917],{"class":804},[741,1174,1109],{"class":755},[741,1176,859],{"class":751},[741,1178,1180],{"class":1179},"sbssI"," 400",[741,1182,952],{"class":804},[741,1184,1185,1188,1190,1193,1195,1197,1200,1202],{"class":743,"line":905},[741,1186,1187],{"class":747},"    return",[741,1189,752],{"class":751},[741,1191,1192],{"class":804}," error",[741,1194,808],{"class":751},[741,1196,765],{"class":751},[741,1198,1199],{"class":768},"empty body",[741,1201,814],{"class":751},[741,1203,1204],{"class":751}," }\n",[741,1206,1207],{"class":743,"line":935},[741,1208,1209],{"class":751},"  }\n",[741,1211,1212],{"class":743,"line":955},[741,1213,779],{"emptyLinePlaceholder":778},[741,1215,1217,1220],{"class":743,"line":1216},13,[741,1218,1219],{"class":747},"  try",[741,1221,798],{"class":751},[741,1223,1225,1228,1231,1233,1235,1237,1239],{"class":743,"line":1224},14,[741,1226,1227],{"class":788},"    const",[741,1229,1230],{"class":755}," events",[741,1232,1127],{"class":751},[741,1234,914],{"class":913},[741,1236,917],{"class":804},[741,1238,921],{"class":755},[741,1240,952],{"class":804},[741,1242,1244,1247,1249,1251,1254],{"class":743,"line":1243},15,[741,1245,1246],{"class":747},"    await",[741,1248,1074],{"class":913},[741,1250,917],{"class":804},[741,1252,1253],{"class":755},"events",[741,1255,952],{"class":804},[741,1257,1259,1261,1263,1265,1267,1270],{"class":743,"line":1258},16,[741,1260,1170],{"class":913},[741,1262,917],{"class":804},[741,1264,1109],{"class":755},[741,1266,859],{"class":751},[741,1268,1269],{"class":1179}," 204",[741,1271,952],{"class":804},[741,1273,1275,1277],{"class":743,"line":1274},17,[741,1276,1187],{"class":747},[741,1278,1279],{"class":751}," null\n",[741,1281,1283,1286,1289,1291,1294,1296],{"class":743,"line":1282},18,[741,1284,1285],{"class":751},"  }",[741,1287,1288],{"class":747}," catch",[741,1290,1106],{"class":804},[741,1292,1293],{"class":755},"err",[741,1295,1162],{"class":804},[741,1297,1165],{"class":751},[741,1299,1301,1303,1305,1307,1309,1312,1315,1317,1320,1322,1325,1328],{"class":743,"line":1300},19,[741,1302,1170],{"class":913},[741,1304,917],{"class":804},[741,1306,1109],{"class":755},[741,1308,859],{"class":751},[741,1310,1311],{"class":755}," err",[741,1313,1314],{"class":751}," instanceof",[741,1316,1036],{"class":926},[741,1318,1319],{"class":751}," ?",[741,1321,1180],{"class":1179},[741,1323,1324],{"class":751}," :",[741,1326,1327],{"class":1179}," 500",[741,1329,952],{"class":804},[741,1331,1333,1335,1337,1339,1341,1343,1346,1348],{"class":743,"line":1332},20,[741,1334,1187],{"class":747},[741,1336,752],{"class":751},[741,1338,1192],{"class":804},[741,1340,808],{"class":751},[741,1342,765],{"class":751},[741,1344,1345],{"class":768},"invalid payload",[741,1347,814],{"class":751},[741,1349,1204],{"class":751},[741,1351,1353],{"class":743,"line":1352},21,[741,1354,1209],{"class":751},[741,1356,1358,1360],{"class":743,"line":1357},22,[741,1359,891],{"class":751},[741,1361,952],{"class":755},[731,1363,1366],{"className":733,"code":1364,"filename":1365,"language":736,"meta":737,"style":737},"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)",[541,1367,1368,1386,1405,1424,1428,1454,1475,1481,1497,1509,1537,1551,1574,1611,1615],{"__ignoreMap":737},[741,1369,1370,1372,1374,1376,1378,1380,1382,1384],{"class":743,"line":744},[741,1371,748],{"class":747},[741,1373,752],{"class":751},[741,1375,1036],{"class":755},[741,1377,759],{"class":751},[741,1379,762],{"class":747},[741,1381,765],{"class":751},[741,1383,769],{"class":768},[741,1385,772],{"class":751},[741,1387,1388,1390,1392,1394,1396,1398,1400,1403],{"class":743,"line":775},[741,1389,748],{"class":747},[741,1391,752],{"class":751},[741,1393,914],{"class":755},[741,1395,759],{"class":751},[741,1397,762],{"class":747},[741,1399,765],{"class":751},[741,1401,1402],{"class":768},"@\u002Flib\u002Ftelemetry-ingest",[741,1404,772],{"class":751},[741,1406,1407,1409,1411,1413,1415,1417,1419,1422],{"class":743,"line":782},[741,1408,748],{"class":747},[741,1410,752],{"class":751},[741,1412,1074],{"class":755},[741,1414,759],{"class":751},[741,1416,762],{"class":747},[741,1418,765],{"class":751},[741,1420,1421],{"class":768},"@\u002Flib\u002Ftelemetry-store",[741,1423,772],{"class":751},[741,1425,1426],{"class":743,"line":801},[741,1427,779],{"emptyLinePlaceholder":778},[741,1429,1430,1432,1435,1437,1440,1442,1445,1447,1450,1452],{"class":743,"line":828},[741,1431,785],{"class":747},[741,1433,1434],{"class":788}," async",[741,1436,910],{"class":788},[741,1438,1439],{"class":913}," POST",[741,1441,917],{"class":751},[741,1443,1444],{"class":920},"request",[741,1446,808],{"class":751},[741,1448,1449],{"class":926}," Request",[741,1451,930],{"class":751},[741,1453,798],{"class":751},[741,1455,1456,1458,1460,1462,1464,1467,1469,1472],{"class":743,"line":838},[741,1457,1121],{"class":788},[741,1459,1124],{"class":755},[741,1461,1127],{"class":751},[741,1463,1130],{"class":747},[741,1465,1466],{"class":755}," request",[741,1468,544],{"class":751},[741,1470,1471],{"class":913},"text",[741,1473,1474],{"class":804},"()\n",[741,1476,1477,1479],{"class":743,"line":882},[741,1478,1219],{"class":747},[741,1480,798],{"class":751},[741,1482,1483,1485,1487,1489,1491,1493,1495],{"class":743,"line":888},[741,1484,1227],{"class":788},[741,1486,1230],{"class":755},[741,1488,1127],{"class":751},[741,1490,914],{"class":913},[741,1492,917],{"class":804},[741,1494,921],{"class":755},[741,1496,952],{"class":804},[741,1498,1499,1501,1503,1505,1507],{"class":743,"line":900},[741,1500,1246],{"class":747},[741,1502,1074],{"class":913},[741,1504,917],{"class":804},[741,1506,1253],{"class":755},[741,1508,952],{"class":804},[741,1510,1511,1513,1516,1519,1521,1524,1526,1529,1531,1533,1535],{"class":743,"line":905},[741,1512,1187],{"class":747},[741,1514,1515],{"class":751}," new",[741,1517,1518],{"class":913}," Response",[741,1520,917],{"class":804},[741,1522,1523],{"class":751},"null,",[741,1525,752],{"class":751},[741,1527,1528],{"class":804}," status",[741,1530,808],{"class":751},[741,1532,1269],{"class":1179},[741,1534,759],{"class":751},[741,1536,952],{"class":804},[741,1538,1539,1541,1543,1545,1547,1549],{"class":743,"line":935},[741,1540,1285],{"class":751},[741,1542,1288],{"class":747},[741,1544,1106],{"class":804},[741,1546,1293],{"class":755},[741,1548,1162],{"class":804},[741,1550,1165],{"class":751},[741,1552,1553,1555,1557,1559,1561,1563,1565,1567,1569,1571],{"class":743,"line":955},[741,1554,1227],{"class":788},[741,1556,1528],{"class":755},[741,1558,1127],{"class":751},[741,1560,1311],{"class":755},[741,1562,1314],{"class":751},[741,1564,1036],{"class":926},[741,1566,1319],{"class":751},[741,1568,1180],{"class":1179},[741,1570,1324],{"class":751},[741,1572,1573],{"class":1179}," 500\n",[741,1575,1576,1578,1580,1582,1585,1587,1590,1592,1594,1596,1598,1600,1603,1605,1607,1609],{"class":743,"line":1216},[741,1577,1187],{"class":747},[741,1579,1518],{"class":755},[741,1581,544],{"class":751},[741,1583,1584],{"class":913},"json",[741,1586,917],{"class":804},[741,1588,1589],{"class":751},"{",[741,1591,1192],{"class":804},[741,1593,808],{"class":751},[741,1595,765],{"class":751},[741,1597,1345],{"class":768},[741,1599,814],{"class":751},[741,1601,1602],{"class":751}," },",[741,1604,752],{"class":751},[741,1606,1528],{"class":755},[741,1608,759],{"class":751},[741,1610,952],{"class":804},[741,1612,1613],{"class":743,"line":1224},[741,1614,1209],{"class":751},[741,1616,1617],{"class":743,"line":1243},[741,1618,958],{"class":751},[731,1620,1623],{"className":733,"code":1621,"filename":1622,"language":736,"meta":737,"style":737},"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)",[541,1624,1625,1645,1663,1682,1701,1705,1721,1725,1759,1783,1789,1805,1817,1836,1850,1872,1902,1906,1912,1916],{"__ignoreMap":737},[741,1626,1627,1629,1631,1634,1636,1638,1640,1643],{"class":743,"line":744},[741,1628,748],{"class":747},[741,1630,752],{"class":751},[741,1632,1633],{"class":755}," Hono",[741,1635,759],{"class":751},[741,1637,762],{"class":747},[741,1639,765],{"class":751},[741,1641,1642],{"class":768},"hono",[741,1644,772],{"class":751},[741,1646,1647,1649,1651,1653,1655,1657,1659,1661],{"class":743,"line":775},[741,1648,748],{"class":747},[741,1650,752],{"class":751},[741,1652,1036],{"class":755},[741,1654,759],{"class":751},[741,1656,762],{"class":747},[741,1658,765],{"class":751},[741,1660,769],{"class":768},[741,1662,772],{"class":751},[741,1664,1665,1667,1669,1671,1673,1675,1677,1680],{"class":743,"line":782},[741,1666,748],{"class":747},[741,1668,752],{"class":751},[741,1670,914],{"class":755},[741,1672,759],{"class":751},[741,1674,762],{"class":747},[741,1676,765],{"class":751},[741,1678,1679],{"class":768},"..\u002Flib\u002Ftelemetry-ingest",[741,1681,772],{"class":751},[741,1683,1684,1686,1688,1690,1692,1694,1696,1699],{"class":743,"line":801},[741,1685,748],{"class":747},[741,1687,752],{"class":751},[741,1689,1074],{"class":755},[741,1691,759],{"class":751},[741,1693,762],{"class":747},[741,1695,765],{"class":751},[741,1697,1698],{"class":768},"..\u002Flib\u002Ftelemetry-store",[741,1700,772],{"class":751},[741,1702,1703],{"class":743,"line":828},[741,1704,779],{"emptyLinePlaceholder":778},[741,1706,1707,1710,1713,1715,1717,1719],{"class":743,"line":838},[741,1708,1709],{"class":788},"const",[741,1711,1712],{"class":755}," app ",[741,1714,795],{"class":751},[741,1716,1515],{"class":751},[741,1718,1633],{"class":913},[741,1720,1474],{"class":755},[741,1722,1723],{"class":743,"line":882},[741,1724,779],{"emptyLinePlaceholder":778},[741,1726,1727,1730,1732,1735,1737,1739,1742,1744,1746,1748,1750,1753,1755,1757],{"class":743,"line":888},[741,1728,1729],{"class":755},"app",[741,1731,544],{"class":751},[741,1733,1734],{"class":913},"post",[741,1736,917],{"class":755},[741,1738,814],{"class":751},[741,1740,1741],{"class":768},"\u002Fapi\u002Ftelemetry\u002Fingest",[741,1743,814],{"class":751},[741,1745,859],{"class":751},[741,1747,1434],{"class":788},[741,1749,1106],{"class":751},[741,1751,1752],{"class":920},"c",[741,1754,930],{"class":751},[741,1756,1114],{"class":788},[741,1758,798],{"class":751},[741,1760,1761,1763,1765,1767,1769,1772,1774,1777,1779,1781],{"class":743,"line":900},[741,1762,1121],{"class":788},[741,1764,1124],{"class":755},[741,1766,1127],{"class":751},[741,1768,1130],{"class":747},[741,1770,1771],{"class":755}," c",[741,1773,544],{"class":751},[741,1775,1776],{"class":755},"req",[741,1778,544],{"class":751},[741,1780,1471],{"class":913},[741,1782,1474],{"class":804},[741,1784,1785,1787],{"class":743,"line":905},[741,1786,1219],{"class":747},[741,1788,798],{"class":751},[741,1790,1791,1793,1795,1797,1799,1801,1803],{"class":743,"line":935},[741,1792,1227],{"class":788},[741,1794,1230],{"class":755},[741,1796,1127],{"class":751},[741,1798,914],{"class":913},[741,1800,917],{"class":804},[741,1802,921],{"class":755},[741,1804,952],{"class":804},[741,1806,1807,1809,1811,1813,1815],{"class":743,"line":955},[741,1808,1246],{"class":747},[741,1810,1074],{"class":913},[741,1812,917],{"class":804},[741,1814,1253],{"class":755},[741,1816,952],{"class":804},[741,1818,1819,1821,1823,1825,1828,1830,1832,1834],{"class":743,"line":1216},[741,1820,1187],{"class":747},[741,1822,1771],{"class":755},[741,1824,544],{"class":751},[741,1826,1827],{"class":913},"body",[741,1829,917],{"class":804},[741,1831,1523],{"class":751},[741,1833,1269],{"class":1179},[741,1835,952],{"class":804},[741,1837,1838,1840,1842,1844,1846,1848],{"class":743,"line":1224},[741,1839,1285],{"class":751},[741,1841,1288],{"class":747},[741,1843,1106],{"class":804},[741,1845,1293],{"class":755},[741,1847,1162],{"class":804},[741,1849,1165],{"class":751},[741,1851,1852,1854,1856,1858,1860,1862,1864,1866,1868,1870],{"class":743,"line":1243},[741,1853,1227],{"class":788},[741,1855,1528],{"class":755},[741,1857,1127],{"class":751},[741,1859,1311],{"class":755},[741,1861,1314],{"class":751},[741,1863,1036],{"class":926},[741,1865,1319],{"class":751},[741,1867,1180],{"class":1179},[741,1869,1324],{"class":751},[741,1871,1573],{"class":1179},[741,1873,1874,1876,1878,1880,1882,1884,1886,1888,1890,1892,1894,1896,1898,1900],{"class":743,"line":1258},[741,1875,1187],{"class":747},[741,1877,1771],{"class":755},[741,1879,544],{"class":751},[741,1881,1584],{"class":913},[741,1883,917],{"class":804},[741,1885,1589],{"class":751},[741,1887,1192],{"class":804},[741,1889,808],{"class":751},[741,1891,765],{"class":751},[741,1893,1345],{"class":768},[741,1895,814],{"class":751},[741,1897,1602],{"class":751},[741,1899,1528],{"class":755},[741,1901,952],{"class":804},[741,1903,1904],{"class":743,"line":1274},[741,1905,1209],{"class":751},[741,1907,1908,1910],{"class":743,"line":1282},[741,1909,891],{"class":751},[741,1911,952],{"class":755},[741,1913,1914],{"class":743,"line":1300},[741,1915,779],{"emptyLinePlaceholder":778},[741,1917,1918,1920,1922],{"class":743,"line":1332},[741,1919,785],{"class":747},[741,1921,1096],{"class":747},[741,1923,1924],{"class":755}," app\n",[554,1926,1928],{"id":1927},"step-3-store-idempotently","Step 3 — Store idempotently",[532,1930,1931,1932,1934],{},"Retries and backlog drains can POST the same ",[541,1933,675],{}," twice. Upsert (or skip) so metrics stay correct:",[731,1936,1939],{"className":733,"code":1937,"filename":1938,"language":736,"meta":737,"style":737},"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",[541,1940,1941,1963,1967,2005,2026,2047,2070,2079,2099,2119,2135,2151,2167,2182,2198,2203,2213,2220,2224,2228,2234,2239],{"__ignoreMap":737},[741,1942,1943,1945,1948,1950,1953,1955,1957,1959,1961],{"class":743,"line":744},[741,1944,748],{"class":747},[741,1946,1947],{"class":747}," type",[741,1949,752],{"class":751},[741,1951,1952],{"class":755}," RunEvent",[741,1954,759],{"class":751},[741,1956,762],{"class":747},[741,1958,765],{"class":751},[741,1960,704],{"class":768},[741,1962,772],{"class":751},[741,1964,1965],{"class":743,"line":775},[741,1966,779],{"emptyLinePlaceholder":778},[741,1968,1969,1971,1973,1975,1977,1979,1981,1983,1985,1988,1991,1994,1997,2000,2003],{"class":743,"line":782},[741,1970,785],{"class":747},[741,1972,1434],{"class":788},[741,1974,910],{"class":788},[741,1976,1074],{"class":913},[741,1978,917],{"class":751},[741,1980,1253],{"class":920},[741,1982,808],{"class":751},[741,1984,1952],{"class":926},[741,1986,1987],{"class":755},"[]",[741,1989,1990],{"class":751},"):",[741,1992,1993],{"class":926}," Promise",[741,1995,1996],{"class":751},"\u003C",[741,1998,1999],{"class":926},"void",[741,2001,2002],{"class":751},">",[741,2004,798],{"class":751},[741,2006,2007,2010,2012,2014,2017,2020,2022,2024],{"class":743,"line":801},[741,2008,2009],{"class":747},"  for",[741,2011,1106],{"class":804},[741,2013,1709],{"class":788},[741,2015,2016],{"class":755}," run",[741,2018,2019],{"class":751}," of",[741,2021,1230],{"class":755},[741,2023,1162],{"class":804},[741,2025,1165],{"class":751},[741,2027,2028,2030,2033,2035,2038,2040,2043,2045],{"class":743,"line":828},[741,2029,1246],{"class":747},[741,2031,2032],{"class":755}," db",[741,2034,544],{"class":751},[741,2036,2037],{"class":755},"telemetryRun",[741,2039,544],{"class":751},[741,2041,2042],{"class":913},"upsert",[741,2044,917],{"class":804},[741,2046,1165],{"class":751},[741,2048,2049,2052,2054,2056,2059,2061,2063,2065,2067],{"class":743,"line":838},[741,2050,2051],{"class":804},"      where",[741,2053,808],{"class":751},[741,2055,752],{"class":751},[741,2057,2058],{"class":804}," idempotencyKey",[741,2060,808],{"class":751},[741,2062,2016],{"class":755},[741,2064,544],{"class":751},[741,2066,675],{"class":755},[741,2068,2069],{"class":751}," },\n",[741,2071,2072,2075,2077],{"class":743,"line":882},[741,2073,2074],{"class":804},"      create",[741,2076,808],{"class":751},[741,2078,798],{"class":751},[741,2080,2081,2084,2086,2088,2090,2093,2095,2097],{"class":743,"line":888},[741,2082,2083],{"class":804},"        tool",[741,2085,808],{"class":751},[741,2087,2016],{"class":755},[741,2089,544],{"class":751},[741,2091,2092],{"class":755},"tool",[741,2094,544],{"class":751},[741,2096,723],{"class":755},[741,2098,825],{"class":751},[741,2100,2101,2104,2106,2108,2110,2112,2114,2117],{"class":743,"line":900},[741,2102,2103],{"class":804},"        version",[741,2105,808],{"class":751},[741,2107,2016],{"class":755},[741,2109,544],{"class":751},[741,2111,2092],{"class":755},[741,2113,544],{"class":751},[741,2115,2116],{"class":755},"version",[741,2118,825],{"class":751},[741,2120,2121,2124,2126,2128,2130,2133],{"class":743,"line":905},[741,2122,2123],{"class":804},"        command",[741,2125,808],{"class":751},[741,2127,2016],{"class":755},[741,2129,544],{"class":751},[741,2131,2132],{"class":755},"command",[741,2134,825],{"class":751},[741,2136,2137,2140,2142,2144,2146,2149],{"class":743,"line":935},[741,2138,2139],{"class":804},"        outcome",[741,2141,808],{"class":751},[741,2143,2016],{"class":755},[741,2145,544],{"class":751},[741,2147,2148],{"class":755},"outcome",[741,2150,825],{"class":751},[741,2152,2153,2156,2158,2160,2162,2165],{"class":743,"line":955},[741,2154,2155],{"class":804},"        durationMs",[741,2157,808],{"class":751},[741,2159,2016],{"class":755},[741,2161,544],{"class":751},[741,2163,2164],{"class":755},"durationMs",[741,2166,825],{"class":751},[741,2168,2169,2172,2174,2176,2178,2180],{"class":743,"line":1216},[741,2170,2171],{"class":804},"        custom",[741,2173,808],{"class":751},[741,2175,2016],{"class":755},[741,2177,544],{"class":751},[741,2179,657],{"class":755},[741,2181,825],{"class":751},[741,2183,2184,2187,2189,2191,2193,2196],{"class":743,"line":1224},[741,2185,2186],{"class":804},"        recordedAt",[741,2188,808],{"class":751},[741,2190,2016],{"class":755},[741,2192,544],{"class":751},[741,2194,2195],{"class":755},"timestamp",[741,2197,825],{"class":751},[741,2199,2200],{"class":743,"line":1243},[741,2201,2202],{"class":751},"      },\n",[741,2204,2205,2208,2210],{"class":743,"line":1258},[741,2206,2207],{"class":804},"      update",[741,2209,808],{"class":751},[741,2211,2212],{"class":751}," {},\n",[741,2214,2215,2218],{"class":743,"line":1274},[741,2216,2217],{"class":751},"    }",[741,2219,952],{"class":804},[741,2221,2222],{"class":743,"line":1282},[741,2223,1209],{"class":751},[741,2225,2226],{"class":743,"line":1300},[741,2227,779],{"emptyLinePlaceholder":778},[741,2229,2230],{"class":743,"line":1332},[741,2231,2233],{"class":2232},"sHwdD","  \u002F\u002F Or forward into evlog's drain pipeline for Axiom \u002F Datadog \u002F OTLP:\n",[741,2235,2236],{"class":743,"line":1352},[741,2237,2238],{"class":2232},"  \u002F\u002F await ingestWideEvents(events.map(run => ({ source: 'telemetry', ...run })))\n",[741,2240,2241],{"class":743,"line":1357},[741,2242,958],{"class":751},[554,2244,2246],{"id":2245},"step-4-rate-limit-at-the-edge","Step 4 — Rate-limit at the edge",[532,2248,2249,2251,2252,2255],{},[541,2250,710],{}," 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 ",[541,2253,2254],{},"machineId"," hash inside the handler if you need tighter control.",[532,2257,2258,2259,2261,2262,2265],{},"Non-2xx responses keep events in the user's outbox; they will retry on the next CLI invocation. Return ",[549,2260,987],{}," (or any ",[541,2263,2264],{},"2xx",") only when storage succeeded.",[2267,2268,2269],"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":737,"searchDepth":775,"depth":775,"links":2271},[2272,2273,2275,2276,2277],{"id":556,"depth":775,"text":557},{"id":714,"depth":775,"text":2274},"Step 1 — Validate with parseIngestBody()",{"id":980,"depth":775,"text":981},{"id":1927,"depth":775,"text":1928},{"id":2245,"depth":775,"text":2246},"Build a server ingestion endpoint for @evlog\u002Ftelemetry — semi-public threat model, parseIngestBody validation, framework routes, idempotent storage, and rate limiting.","md",[2281,2284,2285],{"label":36,"icon":39,"to":414,"color":2282,"variant":2283},"neutral","subtle",{"label":417,"icon":420,"to":418,"color":2282,"variant":2283},{"label":427,"icon":79,"to":428,"color":2282,"variant":2283},{},{"title":422,"icon":425},{"title":527,"description":2278},"VSnPh8oJWcws5A0g6PgxsJg0ylltmCtjOFzxbocoIVg",[2291,2293],{"title":417,"path":418,"stem":419,"description":2292,"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":2294,"icon":79,"children":-1},"RunEvent envelope, extending the schema with collect and custom fields, privacy rules, TELEMETRY.md disclosure, and consent \u002F outbox reliability.",1785675167064]