{"_id":"@aurascope-analytics/forwarder-edge-worker","_rev":"3-6585839c0adfbc20cb290f81e08f50e2","name":"@aurascope-analytics/forwarder-edge-worker","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@aurascope-analytics/forwarder-edge-worker","version":"0.1.0","license":"MIT","_id":"@aurascope-analytics/forwarder-edge-worker@0.1.0","maintainers":[{"name":"naaccs","email":"nic@aurascope.co"}],"homepage":"https://a-analytics.xyz","dist":{"shasum":"be7c2377b442db954cfa64f8da140fd0fde210bc","tarball":"https://registry.npmjs.org/@aurascope-analytics/forwarder-edge-worker/-/forwarder-edge-worker-0.1.0.tgz","fileCount":9,"integrity":"sha512-r93ZUWVI8pBCxB/LEmSExDO9qYT5hO9ABkSW43+9XWDqoCb372N01HIf00xRHss4RLWqbhKBEE0zIo/ck+iJ7Q==","signatures":[{"sig":"MEYCIQC4uYy21oACOqhv4oP+d0VLgc1AHPkPhqVD51FGXEV+9wIhAJl58LzOownC9wGrAZW91JPxWVZyCBUTMpgg6Rybkees","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":19166},"main":"./src/index.ts","type":"module","_from":"file:/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.0.tgz","types":"./src/index.ts","exports":{".":"./src/index.ts","./package.json":"./package.json"},"scripts":{"test":"bun test","build":"tsc --noEmit"},"_npmUser":{"name":"naaccs","email":"nic@aurascope.co"},"_resolved":"/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.0.tgz","_integrity":"sha512-r93ZUWVI8pBCxB/LEmSExDO9qYT5hO9ABkSW43+9XWDqoCb372N01HIf00xRHss4RLWqbhKBEE0zIo/ck+iJ7Q==","_npmVersion":"11.19.0","description":"AuraScope Analytics edge-worker forwarder — a Cloudflare Worker handler that observes cache-served requests and ships them with waitUntil","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@cloudflare/workers-types":"^5.20260715.1"},"_npmOperationalInternal":{"tmp":"tmp/forwarder-edge-worker_0.1.0_1786277041326_0.1868755923938108","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aurascope-analytics/forwarder-edge-worker","version":"0.1.1","license":"MIT","_id":"@aurascope-analytics/forwarder-edge-worker@0.1.1","maintainers":[{"name":"naaccs","email":"nic@aurascope.co"}],"homepage":"https://a-analytics.xyz","dist":{"shasum":"b5a5c0d1cf49547dd3d63c35c279fbf2d699f3cc","tarball":"https://registry.npmjs.org/@aurascope-analytics/forwarder-edge-worker/-/forwarder-edge-worker-0.1.1.tgz","fileCount":9,"integrity":"sha512-ma7P52vj6ECsB0pmH1MS7ganOfRZ/G5pucx/p85Wk4tUtKLwhgWZ70p+k9F2xYXuzsWuYabRDbYCmZViPMH9pg==","signatures":[{"sig":"MEYCIQD4LG0tspBlaU8H6D0OVcsFzNboeThpIzcRs1o07AyEfAIhANrr4r7RGpTNfFfI+QNPeZCKO9USLVOnWvxPZiOTg6VN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":19474},"main":"./src/index.ts","type":"module","_from":"file:/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.1.tgz","types":"./src/index.ts","exports":{".":"./src/index.ts","./package.json":"./package.json"},"scripts":{"test":"bun test","build":"tsc --noEmit"},"_npmUser":{"name":"naaccs","email":"nic@aurascope.co"},"_resolved":"/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.1.tgz","_integrity":"sha512-ma7P52vj6ECsB0pmH1MS7ganOfRZ/G5pucx/p85Wk4tUtKLwhgWZ70p+k9F2xYXuzsWuYabRDbYCmZViPMH9pg==","_npmVersion":"11.19.0","description":"AuraScope Analytics edge-worker forwarder — a Cloudflare Worker handler that observes cache-served requests and ships them with waitUntil","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@cloudflare/workers-types":"^5.20260715.1"},"_npmOperationalInternal":{"tmp":"tmp/forwarder-edge-worker_0.1.1_1786342666577_0.3712014986658885","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aurascope-analytics/forwarder-edge-worker","version":"0.1.2","description":"AuraScope Analytics edge-worker forwarder — a Cloudflare Worker handler that observes cache-served requests and ships them with waitUntil","homepage":"https://a-analytics.xyz","license":"MIT","type":"module","main":"./src/index.ts","types":"./src/index.ts","exports":{".":"./src/index.ts","./package.json":"./package.json"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc --noEmit","test":"bun test"},"devDependencies":{"@cloudflare/workers-types":"^5.20260715.1","typescript":"^5.8.0"},"_id":"@aurascope-analytics/forwarder-edge-worker@0.1.2","_integrity":"sha512-Eka2rvO+7Ad8puw2tt/oE8QNccn7MZ8K2k6pH16ZNTAAxDYYeW0Y9e3nvsjEfYIVq5GULuvQyJlIxufw5pQR/w==","_resolved":"/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.2.tgz","_from":"file:/home/runner/work/_temp/aurascope-analytics-forwarder-edge-worker-0.1.2.tgz","_nodeVersion":"22.14.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-Eka2rvO+7Ad8puw2tt/oE8QNccn7MZ8K2k6pH16ZNTAAxDYYeW0Y9e3nvsjEfYIVq5GULuvQyJlIxufw5pQR/w==","shasum":"f4705a86201d2710f103e2974f16ebc710bab736","tarball":"https://registry.npmjs.org/@aurascope-analytics/forwarder-edge-worker/-/forwarder-edge-worker-0.1.2.tgz","fileCount":9,"unpackedSize":20007,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDW9ubeXK73BmCSUPREeezeDKgiMf4VN0XsdfTdsFglQwIhAPFj6BQlcihEAO3HttiuUKulbkNvJduwoRFgvC86sT+O"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d92b495f-6edb-482e-9285-a685cd607548"}},"directories":{},"maintainers":[{"name":"naaccs","email":"nic@aurascope.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/forwarder-edge-worker_0.1.2_1786413328772_0.5485453860764127"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-09T12:04:01.217Z","modified":"2026-08-11T01:55:29.056Z","0.1.0":"2026-08-09T12:04:01.483Z","0.1.1":"2026-08-10T06:17:46.709Z","0.1.2":"2026-08-11T01:55:28.905Z"},"license":"MIT","homepage":"https://a-analytics.xyz","description":"AuraScope Analytics edge-worker forwarder — a Cloudflare Worker handler that observes cache-served requests and ships them with waitUntil","maintainers":[{"name":"naaccs","email":"nic@aurascope.co"}],"readme":"# Edge-worker forwarder\n\nThis Cloudflare Worker is the server-plane witness for requests handled at the\ncontent delivery network edge, including cache-served requests that never reach an\napplication hook or origin access log. It emits the frozen structured request-event\nshape with per-line `v: 1`, a random per-isolate `iid`, and a monotonic per-isolate\n`n`. It never computes or sends `event_id`; ingest owns that identifier.\n\nThe handler awaits only the tenant’s real origin/next response. It returns that\n`Response` unchanged and schedules capture plus shipping with\n`ctx.waitUntil(...)`, so AuraScope network work does not extend the response hot\npath. A minimal Cloudflare integration is the default export in `src/index.ts`:\n\n```ts\nexport default createWorkerHandler();\n```\n\n## Fail-open: two mechanisms, two jobs (ADR 0037)\n\nA Worker that throws serves the tenant’s visitor a Cloudflare error page, so the\nhandler arms **both** of the runtime’s protections and neither substitutes for\nthe other:\n\n1. `ctx.passThroughOnException()` is the **first statement** of the handler,\n   before the metrics increment, before capture, before the origin fetch. It\n   covers unhandled exceptions in *our* code: the runtime re-issues the request\n   to the origin as though this Worker were not installed. Ordering is the\n   decision — a throw can only degrade that way if the flag was already set.\n   `WaitUntilContext` declares the method **optional** so non-Worker callers\n   (the end-to-end driver, unit tests) stay constructible; that is a testing\n   affordance, pinned as such by a contract test on the exported entrypoint and\n   by a type check in `scripts/check-edge-worker-install.sh`.\n2. Every origin fetch sits inside its **own** `try`/`catch`, because\n   Cloudflare states plainly that (1) does not cover errors from the origin\n   `fetch()`. The catch counts the failure on its own `originFailed` counter,\n   logs it, and returns a **minimal, status-only `502`** — no body, no vendor\n   identifier. It never re-throws: request bodies are streamed rather than\n   buffered, so a re-throw after the body is consumed would leave the fallback\n   re-issuing the request *without* it, turning a visitor’s checkout into an\n   unrelated `4xx`.\n\nAn origin that *answers* is never touched — any status it returns, including its\nown `5xx`, is passed through unchanged. Only a throw reaches the catch, and no\nevent is captured for a request that has no origin status.\n\nDaily-request-limit fall-through on a free Cloudflare plan is a **route**\nsetting (`request_limit_fail_open`), not a runtime call, and is owned by the\ndeploy-on-behalf path — arming pass-through does not satisfy it.\n\nBind `INGEST_URL`, `AURASCOPE_SITE_KEY`, and the secret\n`AURASCOPE_FORWARDER_SK` at deployment. `AURASCOPE_PROBE_MARKER` is an optional\nsynthetic-traffic self-declaration (ADR 0020), sent as the\n`x-aurascope-probe-marker` batch envelope header — a versioned addition to the\nfrozen v1 contract; shipped events (including the user agent) are never mutated\nby it. The raw\n`CF-Connecting-IP` value (falling back to `X-Forwarded-For`) and the full path plus\nquery are shipped; ingest resolves trusted hops and applies its allowlist.\n\nThe edge worker is an unreplayable sender: there is no durable disk and no local\nretry queue. A `200` is a normal acknowledgement. A `202 {\"buffered\":true}` is\nalso success because ingest has accepted ownership into its bounded ADR-0004\nbuffer. Any other response or network error loses the event and increments the\nper-isolate `lost` counter; `401` is also logged. The in-memory `observed`,\n`shipped`, `buffered`, `lost`, and `originFailed` counters are unit-test\nobservability only—durable production metrics must be exported to the Cloudflare\nplatform, and every one of them dies with its isolate, so none of them is a\nfleet-wide figure. `originFailed` is deliberately separate from `lost`: `lost`\nmeans an event we captured never reached ingest, and folding “the tenant’s\norigin was unreachable” into it would make the shipment-loss number unreadable\nfor exactly the tenants having the worst day. No drop header is sent.\n\nThis package publishes to the public npm registry as\n`@aurascope-analytics/forwarder-edge-worker` (ADR 0032), so a tenant adds it to their\nWorkers project as an ordinary dependency:\n\n```sh\nnpm install @aurascope-analytics/forwarder-edge-worker\n```\n\nIt ships `src/` — TypeScript, deliberately: Wrangler compiles TypeScript itself, so a\npre-compiled `dist/` would add a build step that buys a tenant nothing and costs them\nreadable sources at the edge. The `exports` map points at `src/index.ts` for both the\nruntime entry and the types. Operator detail:\n[`docs/distribution.md`](../../docs/distribution.md).\n\nInstall dependencies with `bun install`, type-check with `bun run build`, and run\nthe core under Bun with `bun test`. `scripts/check-edge-worker-install.sh` covers what\nthose cannot: it packs the package, installs the tarball into a throwaway project, and\ntype-checks the public guide's exact import lines under `moduleResolution: bundler` with\n`@cloudflare/workers-types` — the packaging and resolution half of the tenant's path.\nWrangler itself is the deployment runtime and is still not installed or exercised in\nthis repository's continuous-integration lane; that remains an explicit deployment-test\ngap. A deployment may also co-serve the `/px` proxy role, but that routing is\ndeliberately not built here.\n","readmeFilename":"README.md"}