# Install the PulseAI passive Cloudflare collector

This collector records request counts and limited, explicitly unverified traffic labels. It passes website requests to your existing origin Worker and returns its response unchanged. It does not block visitors, require declarations, change caching, verify a provider, identify an underlying model, or count remote tokens.

The initial supported installation is **a customer-owned Cloudflare Worker with an existing private origin Worker connected through a service binding**. Your website hostname must be routed through that collector. This is not a browser script, a WordPress plugin, a Shopify integration, or a generic proxy for an arbitrary URL. Contact [PulseAI Support](mailto:supportpulseai@gmail.com) for assisted installation. Cloudflare account access, deployment permissions and any Cloudflare usage costs are your responsibility; installing these files does not purchase or activate a PulseAI subscription.

## What you need

- Control of the exact website hostname and its Cloudflare zone/Worker routing.
- An existing origin Worker that can be reached through a private `ORIGIN` service binding. Retain its source and rollback configuration. Do not point `ORIGIN` back to this collector or fetch the collector's public hostname from the origin: that creates a loop.
- Cloudflare SQLite Durable Objects enabled for your account, and Wrangler authenticated to the correct account. The outbox is in **your** Cloudflare account, not a shared PulseAI methods service.
- A domain prepared and ownership-verified in your [PulseAI dashboard](https://pulseaianalytics.com/dashboard), its site ID and its per-site ingestion key. A current server-verified subscription is required for events to be accepted. Domain verification and returning from checkout do not grant analytics access.

Public checkout availability is controlled by PulseAI separately. Do not install this on someone else's website, fabricate an entitlement, delete an existing verified property to make a test slot, or create a second unpaid domain. Arrange an authorized, genuinely entitled installation before interpreting a queue as a working paid service.

## 1. Prepare the domain and files

Sign into PulseAI, prepare your exact hostname, and publish the verification file at the HTTPS URL/token shown in the dashboard. Keep the file publicly reachable without redirects. Complete ownership verification before collection. The collector preserves the origin's verification-file response; it does not fabricate ownership proof.

Download these files into a separate local Worker project:

- [pulseai-cloudflare-worker.mjs](https://pulseaianalytics.com/downloads/pulseai-cloudflare-worker.mjs)
- [pulseai-cloudflare-wrangler.jsonc](https://pulseaianalytics.com/downloads/pulseai-cloudflare-wrangler.jsonc), saved locally as `wrangler.jsonc`.

Install Wrangler locally if needed (`npm install --save-dev wrangler`). Review the source and template with the person who maintains your Worker. Do not paste the ingestion key or diagnostics token into either downloadable file, a public repository, screenshots, browser code or a support ticket.

## 2. Configure the customer-owned Worker

Replace the example Worker name, origin service name and variables. Keep `routes: []` and `workers_dev: false` during the first deployment, so no customer traffic is routed before its bindings and secrets are ready.

| Configuration | Value |
|---|---|
| `PULSEAI_SITE_ID` | The site ID belonging to this verified PulseAI property. |
| `PULSEAI_CUSTOMER_HOSTNAME` | Its exact lowercase hostname, such as `www.example.com`, without scheme, port or path. An apex and `www` are different collector hostnames. |
| `PULSEAI_PUBLIC_PATHS_JSON` | Optional JSON list of at most 100 reviewed, static, nonsensitive paths, for example `["/","/pricing","/about"]`. Default `["/"]`. All other paths are counted as `/:other`. |
| `ORIGIN` binding | The existing private origin Worker service. It must not refer to the collector itself. |
| `PULSEAI_OUTBOX` binding | The exported `PulseAIOutbox` class in the collector module. Keep the supplied SQLite migration for the initial deployment. |
| `PULSEAI_INGESTION_KEY` secret | The per-site key shown once by PulseAI. Rotate it through the owning dashboard if lost or exposed. |
| `PULSEAI_DIAGNOSTICS_TOKEN` secret | A distinct, randomly generated 32–256 character token using letters, digits, `_` or `-`. It must differ from the ingestion key. |

Do not place customer names, emails, account IDs, tokens, order numbers or personalized URLs in the public path list. Only exact configured paths are emitted; query strings, fragments and unmatched paths are never sent. Neither IPs, cookies, raw headers, request/response bodies, prompts nor page content are stored in the outbox or delivered to PulseAI. User-agent text is examined transiently to choose a label; it is not transmitted or logged by the collector.

Deploy the unrouted Worker, then set both secrets through Wrangler's interactive secret prompts:

```sh
npx wrangler deploy
npx wrangler secret put PULSEAI_INGESTION_KEY
npx wrangler secret put PULSEAI_DIAGNOSTICS_TOKEN
```

This creates the customer-owned Durable Object binding/migration. It does not route your site yet. Confirm the correct Cloudflare account, origin binding and stored secret names in Cloudflare. Keep the actual secret values private. Do not reset a previously deployed migration or delete a live outbox while changing configuration.

## 3. Route the authorized hostname

Only after the bindings and secrets are ready, replace the empty route list with the exact hostname's route and its zone, for example:

```json
"routes": [{ "pattern": "www.example.com/*", "zone_name": "example.com" }]
```

Have the website owner review existing overlapping routes, custom domains and origin reachability before deployment. Move the intended route to the collector while keeping the origin reachable by the private service binding. An uninstrumented alternate hostname, direct origin URL, CDN response that bypasses the Worker, or uncovered route will not be measured. This template supports one exact hostname/site ID; do not silently combine multiple customers or hostnames under one key. Use separate verified properties and installations within your actual plan limits.

Deploy with `npx wrangler deploy`. Check an ordinary page immediately: status, body, redirects, cookies and caching must match the origin. If they do not, restore the previous routing and contact support. PulseAI analytics failures are handled in background work and should not change website responses.

The collector reserves **GET `/.well-known/pulseai-collector`** on that exact HTTPS hostname for protected diagnostics. Requests to that path require `Authorization: Bearer <PULSEAI_DIAGNOSTICS_TOKEN>` and otherwise return 404. Do not use it for existing website content or publish its token. This is a maintenance endpoint, not a public visitor report.

## 4. Prove delivery before relying on reports

1. Make a small, agreed set of requests to configured and unlisted paths. Include an ordinary browser user agent and a clearly labeled test user agent such as `GPTBot/1.0`; the latter is a **spoofable declaration test**, not proof of an OpenAI visit.
2. Read protected diagnostics with your token using a secure API client. Confirm `counters.enqueued` increases, `pending` drains, `counters.acknowledged` increases and `health.lastAcknowledgedAt` advances. Diagnostics contain counts/status only, not keys, URLs, event payloads or visitor records.
3. In the genuinely entitled owner dashboard, confirm last receipt, observation time, request totals and labels. Unknown traffic stays unknown. A generic bot, Googlebot or Bingbot is not automatically an AI visit.
4. With an agreed small request count, reconcile the website requests, outbox acknowledgements and dashboard totals. Record any uninstrumented paths, restarts or overflow. Exercise retry/duplicate delivery in an isolated, authorized test environment before relying on it for customer reporting; do not revoke live customer credentials or simulate an outage on a production site without approval.

Local implementation tests exercise real workerd SQLite storage, alarms and runtime reload, the actual PulseAI receiver, lost acknowledgements, deduplicated quota, billing rejection and unchanged origin responses. Those checks do not establish a hosted customer installation, provider accuracy, traffic capacity or completed payment. Each installation still needs the checks above.

## Labels and collection limits

- **Declared:** an allowlisted HTTP user-agent token claims OpenAI, Anthropic, Perplexity, Google or Bing. Anyone can spoof those tokens. `Google-Extended` is a robots control token and is not treated as an HTTP product identity.
- **Suspected:** a generic automation user-agent signal, such as a crawler, headless client or script. It may be benign and is not proof of AI. This version has no behavioral/visitor rate inference.
- **Unknown:** neither signal was found. It is not a count of humans; browser-based agents may remain unknown.

The collector emits only request events, source claims, these labels, `action: observed`, origin status and measured origin-response latency. It emits no independently verified identity, conversion, browser vital, token, model, purpose or cache-hit measurement. Review the public [delivery protocol](https://pulseaianalytics.com/downloads/pulseai-ingestion-protocol.md) for the receiver contract.

The observation is scheduled with Cloudflare `waitUntil` after the origin response. **An event is durable only once the outbox confirms its write.** Abrupt runtime termination, unavailable bindings, origin exceptions, platform quotas or failed writes before that point can cause coverage gaps. This does not provide exactly-once observation of every website request. Confirmed outbox events retain immutable IDs and observation timestamps; retries delete only explicit receiver acknowledgements. The receiver separately deduplicates each site/event ID and charges quota for new receipts only.

## Queue health and recovery

The outbox holds at most **10,000 pending events** per installation. A full queue leaves existing events intact and increments `queueOverflow`; new observations cannot be saved. Background write failures produce only rate-limited static warning codes in Cloudflare logs. Do not enable raw request/body/header logging as a workaround.

Alarms send at most **50 events / 32 KiB** per batch to the fixed HTTPS endpoint `https://pulseaianalytics.com/api/events`. Credentials are never followed through redirects. Network failures, malformed acknowledgements, partial acknowledgements and server failures retain unacknowledged events for capped exponential retry (about 5 seconds up to one hour, with jitter). Authentication/domain/subscription errors pause retry for about an hour; fix the configuration/billing cause instead of generating more traffic. Do not expect these retries to drain immediately after a secret rotation.

| Diagnostic | What to do |
|---|---|
| `key_or_domain_action_required` | Check exact site ID, ownership status and the current per-site key. |
| `subscription_action_required` | Confirm a genuine current subscription in PulseAI. A return URL or manual flag is not an entitlement. |
| `monthly_limit_or_rate_limit` | Review your monthly allowance and current plan; queued events do not increase the allowance. |
| `network_or_timeout` / `receiver_rejected` | Check service availability and retry; retain the original outbox. |
| `redirect_refused` | Contact support. Do not redirect credentials or change the endpoint to a different host. |
| `invalid_acknowledgement` | Retain backlog and contact support; an HTTP success alone is not delivery. |
| `configuration_or_storage_error` | Check bindings, variables, secrets and Cloudflare storage availability. |
| `outbox_binding_mismatch` | Restore the original site ID and hostname for this outbox. Do not reassign queued data to a different property. |
| `queueOverflow` / `rejected` counters increasing | Counts have gaps. Investigate promptly and record the affected interval. |

Events older than the receiver's **seven-day** observation window become explicitly counted rejections, not acknowledgements. The most recent 100 rejection IDs/reasons are retained locally in the customer-owned outbox storage for reconciliation; diagnostics expose only their count and last reason. The rejected event payload is removed. Backups, logs and other telemetry in your own Cloudflare account have separate policies. The installer does not delete data from those systems.

The first event durably binds its outbox to the exact site ID and hostname. A later deployment cannot flush old pending events under a replacement site's configuration: it stops delivery and retains them with `outbox_binding_mismatch`. That protection applies to the old object's pending alarms even when new requests select a different object. Restore the original configuration to recover its backlog; create a separate correctly verified installation for a different site/hostname. Do not edit the stored binding or copy queue rows between customers.

For key rotation, rotate the correct site in the owning PulseAI account, update `PULSEAI_INGESTION_KEY` via its secret prompt, and verify later acknowledgements; keep the same site ID and hostname/outbox to retain backlog. For hostname changes or uninstall, contact support, review route coverage and restore the origin route first. Deleting the Worker or Durable Object can destroy pending observations; do not treat deletion as successful delivery. Handle local storage cleanup under your own retention policy after deciding what to do with the remaining backlog.

This is an assisted, limited Cloudflare integration. Hosted installation, paid delivery, lifecycle/revocation behavior and measured capacity must be verified for the actual offer. Contact **supportpulseai@gmail.com** without including ingestion/diagnostics secrets, raw visitor logs or customer personal data.
