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