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