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

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:

$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:

1let mut px: PXEnforcer = PXEnforcer::new(perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME);
2
3px.set_enrich_custom_params_fn(set_enrich_custom_params);
4px.set_additional_activity_handler_fn(additional_activity_handler);
5
6let px_result = px.enforce(&mut req)?;
7if let Some(r) = px_result {
8 return Ok(r);
9};
10
11// ... communicate with the origin server and process the response ...
12
13px.post_enforce(&mut response);

PXEnforcer Setup

Initialize the PXEnforcer structure with the name of the Fastly Config Store. You can use the default name: perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME.

1pub fn new(config_store_name: &str) -> Self

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

1pub 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:

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

Access the PXContext structure through px.ctx():

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

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

1pub type PXEnrichCustomParamsFn =
2 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:

1pub 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:

1use fastly::{Error, Request, Response};
2use perimeterx_fastly_enforcer::{
3 PXModuleMode,
4 pxconfig::{PXConfig, PXCustomParams},
5 pxcontext::PXContext,
6 pxenforce::PXEnforcer,
7};
8
9const ORIGIN_BACKEND: &str = "origin_backend";
10
11// A simple function that sends the request to the origin.
12fn send_to_origin(req: Request) -> Result<Response, Error> {
13 println!("sending to Origin...");
14 match req.send(ORIGIN_BACKEND) {
15 Ok(r) => Ok(r),
16 Err(e) => Err(e.into()),
17 }
18}
19
20// Callback function to set custom parameters.
21fn set_enrich_custom_params(_req: &Request, _conf: &PXConfig, params: &mut PXCustomParams) {
22 params.custom_param3 = "test3".to_string();
23 params.custom_param6 = "test6".to_string();
24}
25
26// Callback function executed after sending page_requested or block activity to the collector.
27fn additional_activity_handler(_req: &Request, _conf: &PXConfig, _ctx: &PXContext) {
28 println!("additional activity handler called");
29}
30
31#[fastly::main]
32fn main(mut req: Request) -> Result<Response, Error> {
33 log_fastly::Logger::builder()
34 .max_level(log::LevelFilter::Info)
35 .default_endpoint("LOG_ENDPOINT")
36 .init();
37
38 let mut px: PXEnforcer = PXEnforcer::new(perimeterx_fastly_enforcer::DEFAULT_CONFIGSTORE_NAME);
39
40 // Usage Example: set several custom parameters, which will be sent to PX Collector
41 px.set_enrich_custom_params_fn(set_enrich_custom_params);
42
43 // Usage Example: set a function executed after sending page_requested or block activity to the collector
44 px.set_additional_activity_handler_fn(additional_activity_handler);
45
46 // Usage Example: set a function to identify "sensitive" requests
47 px.set_is_sensitive_request_fn(|req, _conf| {
48 req.get_url().path().starts_with("/api/v1/user")
49 || req.get_url().path().starts_with("/api/v1/payment")
50 || req.get_url().path().starts_with("/login")
51 });
52
53 // Usage Example: set a function to filter out requests that should not be verified
54 px.set_is_filtered_request_fn(|req, _conf| {
55 req.get_url().path().starts_with("/health")
56 || req.get_url().path().starts_with("/static")
57 || req.get_url().path().starts_with("/assets")
58 });
59
60 // Usage Example: set module mode (Monitor/Blocking) for specific requests
61 if req.get_url().path().starts_with("/test/monitor") {
62 px.set_module_mode(PXModuleMode::Monitor);
63 }
64
65 // execute PX Enforcer for Request
66 let px_result = px.enforce(&mut req)?;
67
68 // print Data Enrichment values, if available
69 if let Some(de) = px.ctx().get_data_enrichment() {
70 log::info!(
71 "PX Data Enrichment: f_kb={}, f_type={}, f_id={}, f_origin={}, ipc_id={:?}, inc_id={:?}, breached_account={}, f_access_token={}",
72 de.get_f_kb(),
73 de.get_f_type(),
74 de.get_f_id(),
75 de.get_f_origin(),
76 de.get_ipc_id(),
77 de.get_inc_id(),
78 de.get_breached_account(),
79 de.get_f_access_token()
80 );
81 } else {
82 log::error!("Data Enrichment is not available");
83 }
84
85 // immediately return, if it's a "blocked" or "first party" response
86 if let Some(r) = px_result {
87 return Ok(r);
88 };
89
90 // ... process Client request ...
91
92 // it's possible to access "PXContext" structure.
93 // Usage Example: send "score" value to the Origin, if "score" is available
94 if let Some(score) = px.ctx().get_score() {
95 req.set_header("x-px-score", score.to_string());
96 } else {
97 log::debug!("Score is not available");
98 }
99
100 // a client function to communicate with the Origin
101 let mut response = send_to_origin(req)?;
102
103 // ... process Origin response ...
104
105 // must be called at the end
106 px.post_enforce(&mut response);
107
108 // we are ok to send response back to client
109 Ok(response)
110}

Migration from 1.x version

Backend configuration

Version 1.x used a single Fastly backend for HUMAN communication. The new 2.x version uses four dedicated backends (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

This change separates the Risk API, activities, client-side assets, and CAPTCHA traffic. Keeping these destinations separate makes routing clearer, supports first-party and CAPTCHA flows, and allows each HUMAN service endpoint to be configured independently in Fastly.

To update an existing Fastly service in the 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, and set the override host to the same value as the address.
  5. Set both First byte (ms) and Between bytes (ms) to 1000 ms for each HUMAN backend.
  6. Save the changes and activate the new Fastly service version.
  7. Remove the old 1.x single HUMAN backend after you confirm the new version is serving traffic correctly.

PXContext fields

Many PXContext fields that were previously always present in 1.x version are now optional because they are only available after specific enforcement paths. For example, a request may not have a score, a Risk API call may not run, or data enrichment may not be returned. Access context through px.ctx() and handle Option<> values returned by the getter methods.

Examples:

1if let Some(score) = px.ctx().get_score() {
2 req.set_header("x-px-score", score.to_string());
3}
1if let Some(vid) = px.ctx().get_vid() {
2 log::info!("vid: {}", vid);
3}
1if let Some(risk_rtt) = px.ctx().get_risk_rtt() {
2 log::info!("Risk API round-trip time: {} ms", risk_rtt);
3}
1if let Some(block_reason) = px.ctx().get_block_reason() {
2 log::info!("Block reason: {}", block_reason);
3}
1if let Some(de) = px.ctx().get_data_enrichment() {
2 log::info!("Data enrichment type: {}", de.get_f_type());
3}