> This page is for Applications Protection.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.humansecurity.com/llms.txt.

# Installing the Enforcer

## Prerequisites

In order to compile and deploy Fastly Compute Package, rust compiler and Fastly CLI must be installed and configured: [Compute services](https://developer.fastly.com/learning/compute/)

## Communicate with the HUMAN Backend

For the Enforcer to communicate with HUMAN services, four backend servers must be added and configured in the Fastly UI (or by using the `contrib/pxbackend.sh` script).
Backend parameters (replace `${APP_ID}` with your HUMAN Application ID):

* Name: `human_sapi`, Address: `sapi-${APP_ID}.perimeterx.net`
* Name: `human_collector`, Address: `collector-${APP_ID}.perimeterx.net`
* Name: `human_client`, Address: `client.perimeterx.net`
* Name: `human_captcha`, Address: `captcha.px-cdn.net`

All HUMAN backends should use SSL/TLS. It is recommended to set both `First byte (ms)` and `Between bytes (ms)` to `1000` ms.

## Module installation

Include perimeterx-fastly-enforcer dependency to Cargo.toml:

```bash
cargo add perimeterx-fastly-enforcer
```

## Module integration

To integrate the HUMAN Rust module into existing Rust code, initialize `PXEnforcer`, register any optional callbacks before calling `enforce`, and then send the request through the Enforcer. If `enforce` returns a response, return it immediately because it is a block or first-party response. Otherwise, continue to the origin and call `post_enforce` before returning the origin response:

```rust
let mut px: PXEnforcer = PXEnforcer::new(
    perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME,
    perimeterx_fastly_enforcer::DEFAULT_SECRETSTORE_NAME,
);

px.set_enrich_custom_params_fn(set_enrich_custom_params);
px.set_additional_activity_handler_fn(additional_activity_handler);

let px_result = px.enforce(&mut req)?;
if let Some(r) = px_result {
    return Ok(r);
};

// ... communicate with the origin server and process the response ...

px.post_enforce(&mut response);
```

## PXEnforcer Setup

Initialize the `PXEnforcer` structure with the name of the Fastly Config Store and the Fastly Secret Store. You can use the default names: `perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME` (`"PXConfig"`) and `perimeterx_fastly_enforcer::DEFAULT_SECRETSTORE_NAME` (`"PXSecrets"`).

When `secret_store_name` is a non-empty string, the Enforcer opens the Fastly Secret Store and overlays these keys (same `px_*` names): `px_app_id`, `px_cookie_secret` and `px_auth_token`. Secret Store values override Config Store values for those fields. Missing secrets are non-fatal and leave the Config Store value (or default) in place. Pass an empty string (`""`) for `secret_store_name` to disable Secret Store loading.

```rust
pub fn new(config_store_name: &str, secret_store_name: &str) -> Self
```

This function takes a request and returns an optional response for block or first-party requests:

```rust
pub fn enforce(&mut self, req: &mut Request) -> Result<Option<Response>, Error>
```

At the end of request processing, call the following function to finalize HUMAN Enforcer response handling:

```rust
pub fn post_enforce(&mut self, resp: &mut Response)
```

Access the `PXContext` structure through `px.ctx()`:

```rust
// Send the score value to the origin, if it is available.
if let Some(score) = px.ctx().get_score() {
    req.set_header("x-px-score", score.to_string());
}
```

To set custom parameter values, use the following callback type:

```rust
pub type PXEnrichCustomParamsFn =
    fn(req: &Request, conf: &PXConfig, params: &mut PXCustomParams);
```

where:

* `req`: `fastly::Request`
* `conf`: `PXConfig`
* `params`: modifiable structure with `custom_param1` through `custom_param10` fields

To register the custom parameters callback, use the following setter:

```rust
pub fn set_enrich_custom_params_fn(&mut self, f: PXEnrichCustomParamsFn)
```

## Sample code

This example shows how to use the HUMAN Rust module with custom parameter enrichment, additional activity handling, sensitive request detection, filtered request detection, request-specific module mode, and context access:

```rust
use fastly::{Error, Request, Response};
use perimeterx_fastly_enforcer::{
    PXModuleMode,
    pxconfig::{PXConfig, PXCustomParams},
    pxcontext::PXContext,
    pxenforce::PXEnforcer,
};

const ORIGIN_BACKEND: &str = "origin_backend";

// A simple function that sends the request to the origin.
fn send_to_origin(req: Request) -> Result<Response, Error> {
    println!("sending to Origin...");
    match req.send(ORIGIN_BACKEND) {
        Ok(r) => Ok(r),
        Err(e) => Err(e.into()),
    }
}

// Callback function to set custom parameters.
fn set_enrich_custom_params(_req: &Request, _conf: &PXConfig, params: &mut PXCustomParams) {
    params.custom_param3 = "test3".to_string();
    params.custom_param6 = "test6".to_string();
}

// Callback function executed after sending page_requested or block activity to the collector.
fn additional_activity_handler(_req: &Request, _conf: &PXConfig, _ctx: &PXContext) {
    println!("additional activity handler called");
}

#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
    log_fastly::Logger::builder()
        .max_level(log::LevelFilter::Info)
        .default_endpoint("LOG_ENDPOINT")
        .init();

    let mut px: PXEnforcer = PXEnforcer::new(
        perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME,
        perimeterx_fastly_enforcer::DEFAULT_SECRETSTORE_NAME,
    );

    // Usage Example: set several custom parameters, which will be sent to PX Collector
    px.set_enrich_custom_params_fn(set_enrich_custom_params);

    // Usage Example: set a function executed after sending page_requested or block activity to the collector
    px.set_additional_activity_handler_fn(additional_activity_handler);

    // Usage Example: set a function to identify "sensitive" requests
    px.set_is_sensitive_request_fn(|req, _conf| {
        req.get_url().path().starts_with("/api/v1/user")
            || req.get_url().path().starts_with("/api/v1/payment")
            || req.get_url().path().starts_with("/login")
    });

    // Usage Example: set a function to filter out requests that should not be verified
    px.set_is_filtered_request_fn(|req, _conf| {
        req.get_url().path().starts_with("/health")
            || req.get_url().path().starts_with("/static")
            || req.get_url().path().starts_with("/assets")
    });

    // Usage Example: set module mode (Monitor/Blocking) for specific requests
    if req.get_url().path().starts_with("/test/monitor") {
        px.set_module_mode(PXModuleMode::Monitor);
    }

    // execute PX Enforcer for Request
    let px_result = px.enforce(&mut req)?;

    // print Data Enrichment values, if available
    if let Some(de) = px.ctx().get_data_enrichment() {
        log::info!(
            "PX Data Enrichment: f_kb={}, f_type={}, f_id={}, f_origin={}, ipc_id={:?}, inc_id={:?}, breached_account={}, f_access_token={}",
            de.get_f_kb(),
            de.get_f_type(),
            de.get_f_id(),
            de.get_f_origin(),
            de.get_ipc_id(),
            de.get_inc_id(),
            de.get_breached_account(),
            de.get_f_access_token()
        );
    } else {
        log::error!("Data Enrichment is not available");
    }

    // immediately return, if it's a "blocked" or "first party" response
    if let Some(r) = px_result {
        return Ok(r);
    };

    // ... process Client request ...

    // it's possible to access "PXContext" structure.
    // Usage Example: send "score" value to the Origin, if "score" is available
    if let Some(score) = px.ctx().get_score() {
        req.set_header("x-px-score", score.to_string());
    } else {
        log::debug!("Score is not available");
    }

    // a client function to communicate with the Origin
    let mut response = send_to_origin(req)?;

    // ... process Origin response ...

    // must be called at the end
    px.post_enforce(&mut response);

    // we are ok to send response back to client
    Ok(response)
}
```

# Migrating from 1.x to 2.x

This guide walks you through upgrading the HUMAN Enforcer on Fastly from version 1.x to 2.x. There are three changes to make:

1. **Backend configuration** — replace the single HUMAN backend with four dedicated backends.
2. **Secret Store configuration** — optionally move sensitive values from the Config Store to a Secret Store.
3. **PXContext fields** — update custom code to handle fields that are now optional.

## What changed at a glance

| Area                | 1.x                       | 2.x                                                            |
| ------------------- | ------------------------- | -------------------------------------------------------------- |
| HUMAN backends      | One shared Fastly backend | Four dedicated backends (Risk API, collector, client, CAPTCHA) |
| Secrets             | Fastly Config Store only  | Fastly Secret Store can overlay Config Store values            |
| `PXContext` getters | Fields always present     | Fields optional; getters return `Option<>`                     |

## Prerequisites

* Your HUMAN **Application ID** (`${APP_ID}`). Substitute it wherever `${APP_ID}` appears below.
* Access to the Fastly service that runs the Enforcer, with permission to edit and activate service versions.
* If you plan to use a Secret Store, permission to create and manage Fastly Secret Stores.

## 1. Backend configuration

Version 1.x used a single Fastly backend for HUMAN communication. Version 2.x uses four dedicated backends. This separates Risk API, activity collection, client-side assets, and CAPTCHA traffic. Keeping these destinations separate makes routing clearer, supports first-party and CAPTCHA flows, and lets each HUMAN service endpoint be configured independently in Fastly.

Add the following backends (replace `${APP_ID}` with your HUMAN Application ID):

| Backend name      | Address                              | Purpose                          |
| ----------------- | ------------------------------------ | -------------------------------- |
| `human_sapi`      | `sapi-${APP_ID}.perimeterx.net`      | Risk API                         |
| `human_collector` | `collector-${APP_ID}.perimeterx.net` | Activities                       |
| `human_client`    | `client.perimeterx.net`              | Client-side assets (first-party) |
| `human_captcha`   | `captcha.px-cdn.net`                 | CAPTCHA                          |

### Update an existing Fastly service (web UI)

1. Open the Fastly service that runs the Enforcer.
2. Clone the active service version so the backend changes can be edited.
3. Open the **Origins** configuration page.
4. Add the four backends listed above. For each backend:
   * Set the **backend name** and **address**.
   * Enable **SSL/TLS**.
   * Set the **override host** to the same value as the address.
5. Set both **First byte (ms)** and **Between bytes (ms)** to `1000` for each HUMAN backend.
6. Save the changes and **activate** the new Fastly service version.
7. After you confirm the new version is serving traffic correctly, remove the old 1.x single HUMAN backend.

> **Tip:** Keep the old 1.x backend in place until step 7. Removing it only after verifying the new version lets you roll back to the previous service version if anything looks wrong.

## 2. Secret Store configuration

In version 1.x, sensitive values were stored in the Fastly **Config Store**. In version 2.x, sensitive values can be stored in a Fastly **Secret Store**.

When the Enforcer is initialized with a non-empty Secret Store name (default: `PXSecrets`), it overlays the following fields from the Secret Store:

| Field              |
| ------------------ |
| `px_app_id`        |
| `px_cookie_secret` |
| `px_auth_token`    |

Behavior to keep in mind:

* **Secret Store values override Config Store values** for the fields listed above.
* **Missing secrets are non-fatal.** If a secret is absent, the existing Config Store value (or the default) stays in place.
* **To disable secret loading,** pass an empty string for the Secret Store name.

## 3. PXContext fields

Many `PXContext` fields that were always present in 1.x are now **optional** (`Option<T>`) in 2.x, because they are only populated after specific enforcement paths run. For example, a request may not have a `score`, a Risk API call may not run, or data enrichment may not be returned. Setting `score` to 0 is not a valid substitute for an absent score, because 0 is a valid score value. That is why the getters now return `Option<T>` instead of `T`.

Access the context through `px.ctx()` and handle the `Option<>` values returned by the getter methods. The examples below show the recommended pattern for the most common getters.

Read the score (only present when scoring ran):

```rust
if let Some(score) = px.ctx().get_score() {
 req.set_header("x-px-score", score.to_string());
}
```

Read the visitor ID:

```rust
if let Some(vid) = px.ctx().get_vid() {
 log::info!("vid: {}", vid);
}
```

Read the Risk API round-trip time (only present when the Risk API was called):

```rust
if let Some(risk_rtt) = px.ctx().get_risk_rtt() {
 log::info!("Risk API round-trip time: {} ms", risk_rtt);
}
```

Read the block reason (only present when the request was blocked):

```rust
if let Some(block_reason) = px.ctx().get_block_reason() {
 log::info!("Block reason: {}", block_reason);
}
```

Read data enrichment (only present when enrichment was returned):

```rust
if let Some(de) = px.ctx().get_data_enrichment() {
 log::info!("Data enrichment type: {}", de.get_f_type());
}
```

## Verify the migration

After activating the new service version, confirm the upgrade is healthy before removing the old backend:

* Traffic is being served normally with no increase in errors.
* The four HUMAN backends show healthy connections in the Fastly dashboard.
* If you enabled the Secret Store, confirm the overlaid fields resolve correctly (for example, requests authenticate and are scored as expected).
* Any custom code that reads `PXContext` compiles and behaves correctly with the new `Option<>` getters.

## Roll back

Because you cloned the active version, you can revert by reactivating the previous Fastly service version at any time before the old backend is removed.