# Cloudflare Workers (npm)

> Add Centinel bot detection to an existing Cloudflare Worker using the npm package.

Source: https://docs.centinelanalytica.com/platforms/cdn-edge/cloudflare-worker-npm

## Overview

This guide covers adding Centinel to an existing Cloudflare Worker project via the npm package.
Requests flow through the Centinel validator before reaching your worker, blocking bots while letting legitimate traffic through.

> **Looking for the standalone version?:** If you don't have an existing worker and want to paste a single file into the Cloudflare dashboard, see the [Cloudflare Workers (standalone)](https://docs.centinelanalytica.com/platforms/cdn-edge/cloudflare-worker-standalone.md) guide instead.

## Prerequisites

* Secret key from your [dashboard](https://docs.centinelanalytica.com/install/dashboard.md)
* An existing Cloudflare Worker project using Wrangler

## Install

```bash
npm install @centinel/cloudflare-worker
```

## Configure

1. **Set your secret key**

   Use a Wrangler secret. Do not add `CENTINEL_SECRET_KEY` to `[vars]`. A committed
   `wrangler.toml` can expose the key.

   ```bash
   npx wrangler secret put CENTINEL_SECRET_KEY
   ```

2. **Wrap your worker**

   Import `activateCentinel` and wrap your existing worker:

   ```javascript
   import { activateCentinel } from '@centinel/cloudflare-worker';

   const myWorker = {
       async fetch(request, env, ctx) {
           // Your existing worker code
           return new Response('Hello from my app');
       },
   };

   export default activateCentinel(myWorker);
   ```

   Requests now go through the Centinel validator before reaching your worker. Bots are blocked; legitimate traffic passes through.

### Options

Pass a second argument to `activateCentinel`:

```javascript
export default activateCentinel(myWorker, {
    // Only validate paths matching this pattern (tested against pathname)
    protectedPathsInclusion: /\/api(\/|$)/,

    // Also validate requests whose query string matches this pattern
    protectedQueryInclusion: /[?&]cat=/,

    // Skip paths matching this pattern (default: static assets like .css, .js, .png)
    protectedPathsExclusion: /\.(css|js|png|jpg|woff2)$/i,

    // Validator API timeout in milliseconds (default: 500)
    timeout: 5000,

    // Structured JSON debug logging (default: false)
    enableDebugging: true,
});
```

> **Path matching:** `protectedPathsInclusion` and `protectedPathsExclusion` are tested against the URL pathname (e.g. `/api/users`), not the full URL. Query strings and hostname are ignored.

> **Query matching:** The worker tests `protectedQueryInclusion` against the query string with its leading `?`, for example `?cat=nb&sort=p`. The worker decodes percent-encoded parameter names first. The worker validates a request when the path pattern or the query pattern matches. The worker applies `protectedPathsExclusion` first.

### Environment variables

Options can be overridden per-request via environment variables. Set in `wrangler.toml`, the dashboard, or `.dev.vars`:

| Variable                    | What it does                      | Default                                            |
| --------------------------- | --------------------------------- | -------------------------------------------------- |
| `CENTINEL_SECRET_KEY`       | API key sent to the validator     | (none, required)                                   |
| `CENTINEL_VALIDATOR_URL`    | Validator API endpoint            | `https://validator.centinelanalytica.com/validate` |
| `CENTINEL_TIMEOUT`          | Validator timeout in milliseconds | `500`                                              |
| `CENTINEL_ENABLE_DEBUGGING` | Set to `"true"` for debug logs    | `false`                                            |

Env vars take precedence over options passed to `activateCentinel()`, so you can use different settings per environment without changing code.

### How requests flow

1. Request arrives at your Cloudflare Worker
2. `activateCentinel` checks the path against inclusion/exclusion patterns
3. If protected, it POSTs request metadata (URL, method, IP, headers, `_centinel` cookie) to the validator API
4. The validator returns a decision:

**allow**

Request is forwarded to your worker. If the validator set cookies, they're added to the response.

```text
Client → Centinel → Validator (allow) → Your Worker → Client
```

**block**

Your worker is not called. The client receives an HTML error page with the status code from the validator (default 403).

```text
Client → Centinel → Validator (block) → 403 HTML page
```

**redirect**

Your worker is not called. The client receives a base64-decoded interstitial page from the validator.

```text
Client → Centinel → Validator (redirect) → Interstitial page
```

### Fail-open behavior

If the validator is down, slow, or returns an error, the worker forwards the request without validation. This keeps the site available.

Centinel sends each request to your worker one time. CORS preflight (`OPTIONS`) requests do not go to the validator.

A bad secret key, an authentication failure, or a validator response error can also forward a protected request to your origin without validation.

After repeated failures, the worker uses exponential backoff (1s, 2s, 4s, ... up to 5 minutes). This prevents repeated calls to a broken endpoint.

### Standalone mode

If you don't have an existing worker, the default export works on its own:

```javascript
import centinelWorker from '@centinel/cloudflare-worker';
export default centinelWorker;
```

### TypeScript

Type definitions are included:

```typescript
import { activateCentinel, CentinelOptions, CentinelHandler } from '@centinel/cloudflare-worker';

const options: CentinelOptions = {
    protectedPathsInclusion: /\/api\//,
    timeout: 5000,
};

export default activateCentinel(myWorker, options);
```

## Verify

Visit your site and confirm normal traffic works. A response includes
`Server-Timing: validator;dur=<ms>` when the worker gets a validator duration; a network error has
no timing header.

For log output, open **Workers dashboard → Your worker → Logs → Begin log stream** and look for
`"service": "CentinelAnalytics"` entries. Set `CENTINEL_ENABLE_DEBUGGING = "true"` for a JSON line
at each step.

## Changelog

* **v1.4.0** — Query string inclusion option
* **v1.3.0** — 500 ms default validator timeout
* **v1.2.1** — Edge case fixes
