# Akamai EdgeWorkers

> Deploy Centinel Analytica on your Akamai property using EdgeWorkers.

Source: https://docs.centinelanalytica.com/platforms/cdn-edge/akamai-edgeworkers

## Overview

This guide walks through deploying Centinel Analytica server-side validation on Akamai using EdgeWorkers.
Property Manager changes take 15-30 minutes to propagate across the Akamai network.

> **Have an existing EdgeWorker?:** If your property already runs an EdgeWorker on the requests you want to protect, follow [Run alongside an existing EdgeWorker](#run-alongside-an-existing-edgeworker) instead of attaching a second one.

## Prerequisites

* Centinel secret key
* Akamai Control Center access, with the EdgeWorkers add-on enabled
* Property Manager read-write access for the property you want to protect
* The Advanced Metadata privilege (open a support ticket if your account lacks it)

## Install

1. **Download the package**

   1. Download [`centinel-akamai-edgeworker.tgz`](https://docs.centinelanalytica.com/downloads/centinel-akamai-edgeworker.tgz).
   2. Extract the archive to get `main.js` and `bundle.json`.

2. **Create the EdgeWorker**

   1. Log into **Akamai Control Center**.
   2. Go to **CDN → EdgeWorkers**.
   3. Click **Create EdgeWorker ID**.
   4. Enter a name (e.g. `centinel-protection`).
   5. Select your **Group**, and choose **Dynamic Compute** (Tier 200) as the resource tier.
   6. Click **Create EdgeWorker ID**.

3. **Upload and activate the code**

   1. Select the EdgeWorker you just created.
   2. Click **Create Version**.
   3. Upload the `main.js` and `bundle.json` files.
   4. Click **Create Version**.
   5. Activate the new version on the staging and production networks.

## Configure

> **Rule order:** Keep the Centinel rules at the bottom of your rule tree. When two rules set the same behavior, the bottom-most match wins, so a later origin or caching rule would override them.

1. **Configure the validator route**

   Route `/centinel-validate` to the Centinel API.

   1. Go to **CDN → Properties** and select your property.
   2. Click **Edit New Version** to create a draft.
   3. Under **Property Configuration**, click **Add Rule**.
   4. Name the rule `Centinel Validator`.
   5. Add a **Criteria**:
      * **Match Type**: Path
      * **Match Operator**: matches one of
      * **Value**: `/centinel-validate`
   6. Add a **Behavior** → **Origin Server**:
      * **Origin Server Hostname**: `validator.centinelanalytica.com`
      * **Forward Host Header**: Origin Hostname
      * **Origin SSL Certificate Verification**: Yes
   7. Add a **Behavior** → **Allow POST**:
      * **Behavior**: Allow
      * **Allow POST without Content-Length header**: No
   8. Add a **Behavior** → **Modify Outgoing Request Path**:
      * **Action**: Replace Entire Path
      * **Replace With**: `/validate`
   9. Add a **Behavior** → **Caching**:
      * **Caching Option**: No Store
   10. Click **Save**.

   > **Watch out:** Do **not** apply the EdgeWorker behavior to the `/centinel-validate` rule.

2. **Set Property Manager variables**

   1. In your property configuration, go to **Property Variables**.
   2. Add:

   | Variable                        | Value                    |
   | ------------------------------- | ------------------------ |
   | `PMUSER_EW_CENTINEL_SECRET_KEY` | Your Centinel secret key |
   | `PMUSER_EW_CENTINEL_FLOW`       | Leave empty              |

   3. (Optional) Add additional variables from the configuration reference below.
   4. Click **Save**.

   > **Variable visibility:** Set variables to **Hidden** (not Sensitive) in Property Manager.

3. **Attach the EdgeWorker**

   1. Under **Property Configuration**, click **Add Rule** and name it
      `Centinel Protection`.
   2. Add a **Criteria**:
      * **Match Type**: File Extension
      * **Match Operator**: is not one of
      * **Case Sensitive**: Off
      * **Value**: the file types Centinel skips:

   ```text
   avi avif bmp css eot flac flv gif gz ico jpeg jpg js json
   less map mka mkv mov mp3 mp4 mpeg mpg ogg ogm opus otf png
   svg svgz swf ttf wav webm webp woff woff2 xml zip
   ```

   3. Add a **Behavior** → **EdgeWorkers** and select your
      `centinel-protection` EdgeWorker ID.
   4. Add a **Behavior** → **Advanced** with this metadata:

   ```xml
   <edgeservices:cookie.pass-set-cookie-policy>ALWAYS</edgeservices:cookie.pass-set-cookie-policy>
   ```

   5. Click **Save**.

   Your pages stay cacheable. The EdgeWorker checks every request before the cache lookup and adds
   the `_centinel` cookie to each response after it, so the cookie is never stored in
   the cache.

   > **Scope:** To protect only part of your site, add a **Path** criterion to this rule.

4. **Activate the property**

   1. Click **Activate** in Property Manager.
   2. Select **Staging** for initial testing, or **Production** when ready.
   3. Add activation notes and click **Activate**.
   4. Wait for activation to complete (15-30 minutes).

### Run alongside an existing EdgeWorker

Akamai runs one EdgeWorker per request. To add Centinel to rules that already run your own
EdgeWorker, use Akamai
[flexible composition](https://techdocs.akamai.com/edgeworkers/docs/flexible-composition):
your EdgeWorker imports Centinel as a module.

1. **Activate Centinel without attaching it**

   1. Create, upload, and activate the Centinel EdgeWorker as described in
      [Install](#install).
   2. Complete the Configure steps above, but leave the **EdgeWorkers** behavior out
      of the `Centinel Protection` rule. Your EdgeWorker stays on its own rule. If that
      rule covers only part of the property, add its criteria to `Centinel Protection`.

2. **Add Centinel as a dependency**

   In your EdgeWorker's `bundle.json`, add a `dependencies` entry, keep your
   existing fields, and bump `edgeworker-version` as usual. Replace `12345`
   with the Centinel EdgeWorker ID. The key
   `centinel` is the module name you import in the next step.

   ```json
   {
     "edgeworker-version": "2.0.0",
     "dependencies": {
       "centinel": {
         "edgeWorkerId": 12345,
         "version": "active"
       }
     }
   }
   ```

3. **Call Centinel from your handlers**

   In your EdgeWorker's `main.js`, call Centinel before your own code, so blocked requests
   never reach it:

   ```js
   import * as centinel from "centinel";

   export async function onClientRequest(request) {
     await centinel.onClientRequest(request);
     // Centinel responded: return, or a later respondWith() replaces it.
     if (request.wasTerminated()) return;

     // Your existing onClientRequest code.
   }

   export async function onClientResponse(request, response) {
     centinel.onClientResponse(request, response);

     // Your existing onClientResponse code, if any.
   }
   ```

   Centinel uses only these two handlers. Leave your other handlers as they are.

4. **Activate your EdgeWorker**

   1. Upload the updated bundle as a new version of your EdgeWorker.
   2. Activate it on staging and check it with the steps in [Verify](#verify).
   3. Activate it on production.

Before you activate, check:

* **Resource tier**: use Dynamic Compute (Tier 200) or higher for your EdgeWorker. Its
  [limits](https://techdocs.akamai.com/edgeworkers/docs/limitations#flexible-composition-limits)
  cover your code and Centinel together: 4 HTTP sub-requests, 20 ms of CPU, and 5.5 s of wall time
  per event handler. Centinel uses one sub-request in `onClientRequest` and waits up to
  `PMUSER_EW_CENTINEL_TIMEOUT` for it.
* **Property variables**: all `setVariable()` calls in one event handler share a budget of 1,024
  characters, names included, and `setVariable()` throws once it is spent. Centinel uses up to about
  950 of them in `onClientRequest`. If your code also sets variables there, ask your Akamai
  representative to raise the limit to 4,000.
* **Updates**: with `"version": "active"`, activating a new Centinel version reactivates your
  EdgeWorker automatically. To decide when Centinel changes,
  [import a fixed revision](https://techdocs.akamai.com/edgeworkers/docs/review-a-static-activation)
  instead.
* **Permissions**: Akamai lets a parent import children from the same account. Whoever activates
  your EdgeWorker needs Publisher access to it and Viewer access to the Centinel EdgeWorker.
* **subWorkers**: if your `bundle.json` sets `invoke-for-edgeworker-clients` to `true`, turn the
  EdgeWorkers behavior off on the `Centinel Validator` rule, so your EdgeWorker does not run on the
  validator call.

### Configuration reference

#### Required variables

| Variable                        | Type     | Description                                      |
| ------------------------------- | -------- | ------------------------------------------------ |
| `PMUSER_EW_CENTINEL_SECRET_KEY` | `string` | Your Centinel secret key for API authentication. |

#### Optional variables

| Variable                                       | Type     | Default | Description                                                               |
| ---------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------- |
| `PMUSER_EW_CENTINEL_TIMEOUT`                   | `number` | `500`   | Validator timeout in milliseconds.                                        |
| `PMUSER_EW_CENTINEL_PROTECTED_PATHS_INCLUSION` | `string` | –       | Regex pattern to protect only matching paths. Leave empty to protect all. |
| `PMUSER_EW_CENTINEL_PROTECTED_PATHS_EXCLUSION` | `string` | –       | Regex pattern to skip validation. Static assets are excluded by default.  |
| `PMUSER_EW_CENTINEL_ENABLE_DEBUGGING`          | `string` | `false` | Set to `true` to write JSON debug lines to the EdgeWorker log.            |

#### Notes

* **Timeout**: 500ms by default, adjustable with `PMUSER_EW_CENTINEL_TIMEOUT`.
* **Logging**: Uses Akamai's built-in `logger` module. Check EdgeWorker logs in Control Center.
* **Fails open**: any validator error, timeout, unparseable response, or missing secret key allows the request through. An unreachable validator means unprotected traffic, not blocked traffic.
* **Challenge size**: a block or interstitial page larger than 2000 characters cannot be delivered. Blocks fall back to a generic page, and oversized challenges are skipped entirely.

## Verify

* Browse your site to confirm traffic flows normally.
* Check that static assets (`.js`, `.css`, `.png`, etc.) load without delays.
* Confirm a protected response carries a `Server-Timing: validator;dur=...` header.
* On a page Akamai caches, send two requests with a browser `User-Agent`, no cookies, and the header `Pragma: akamai-x-cache-on`. The second should show `X-Cache: TCP_HIT` or `TCP_MEM_HIT`. Each response should carry its own `_centinel` value.
* If the `Server-Timing` header is missing, send a `GET` to `/centinel-validate` on your protected hostname: it should return `405 Method Not Allowed`. Repeat as a `POST`; a `403 Forbidden` means the rule still needs the **Allow POST** behavior.
* For detail, set `PMUSER_EW_CENTINEL_ENABLE_DEBUGGING` to `true` and read the JSON log lines in **Control Center → EdgeWorkers → Logs**.

## Changelog

* **v3.0.5** — Current release, rebuilt from source
* **v3.0.4** — Fixed challenge cookie delivery
* **v3.0.2** — Crawler-aware handling
* **v3.0.1** — Response handling update
* **v3.0.0** — Fixed timeout, logging refactor
* **v2.0.1** — Logging and error handling
* **v2.0.0** — Cookie handling improvements
* **v1.0.0** — Initial release
