In WACElib, a transaction represents one complete HTTP request/response cycle. Every request your WAF inspects is given a unique identifier and passes through a fixed sequence of operations: the library initializes tracking state, invokes ML model plugins over one or more payload segments, waits for all synchronous results, runs a decision plugin, and finally releases all resources. Understanding this lifecycle is essential to integrating WACElib correctly — calling operations out of order or skipping cleanup causes resource leaks and undefined behavior.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tilsor/ModSecIntl_wace_lib/llms.txt
Use this file to discover all available pages before exploring further.
Operation order
The four core functions must be called in strict order for every request:Init (once at process startup)
Call
Init once when your application starts. It initializes the global ConfigStore, loads all model and decision plugins from the configuration, establishes the NATS connection, and wires up OpenTelemetry metrics.Init must not be called more than once per process. It creates the singleton ConfigStore and returns an error if one already exists.InitTransaction (once per request)
Call The transaction ID must be unique across all concurrent in-flight requests.
InitTransaction with a unique identifier before any analysis begins. Internally, this stores a transactionSync entry in the global analysisMap (a sync.Map) with a zeroed counter and a fresh unbuffered channel. It also calls plugins.InitTransaction, which allocates a sync.Map inside PluginManager.results to accumulate model outputs for this transaction.Analyze (one or more times)
Call Each Valid
Analyze for each payload segment you want scored. You pass the plugin type as a string (matching a configstore.ModelPluginType), the transaction ID, an pluginmanager.HTTPPayload, and a slice of model plugin IDs to invoke.Analyze call atomically increments the transactionSync.Counter for this transaction, then launches callPlugins in a goroutine. You may call Analyze multiple times with different plugin types before calling CheckTransaction.modelsTypeAsString values map directly to configstore.ModelPluginType:| String value | Meaning |
|---|---|
"RequestHeaders" | Request line and headers only |
"RequestBody" | Request body only |
"AllRequest" | Full request (headers + body) |
"ResponseHeaders" | Response status line and headers |
"ResponseBody" | Response body only |
"AllResponse" | Full response (headers + body) |
"Everything" | Entire request and response |
CheckTransaction (after each batch of Analyze calls)
Call
CheckTransaction to wait for all pending model goroutines to complete and then invoke the decision plugin. It reads the transactionSync.Counter set by Analyze, drains exactly that many messages from the channel, resets the counter to zero, then calls plugins.CheckResult which feeds accumulated ModelResults into the decision plugin’s CheckResults function.CheckTransaction returns (bool, error). The boolean is true when the decision plugin determines the request should be blocked.Because CheckTransaction resets the counter to zero after draining, you can call it again after more Analyze calls — for example, after inspecting request headers in phase 1, then the response body in phase 2.Complete example
The following example mirrors theTestAnalyzeRequestInParts test and shows a full transaction that analyzes request headers and body separately before making a single decision:
Multiple CheckTransaction calls
WACElib is designed to work with WAFs that process requests in phases. You can interleaveAnalyze and CheckTransaction calls within the same transaction — for example, checking headers in phase 1 and deciding whether to continue to phase 2 before analyzing the response:
CheckTransaction resets the internal counter after each call, so the next batch of Analyze calls starts a fresh count.
All transaction state in WACElib is stored in
sync.Map instances (analysisMap in the core package and PluginManager.results). This means concurrent calls across different transaction IDs are safe without external locking. However, all calls for a single transaction ID must be serialized by the caller — do not call Analyze and CheckTransaction concurrently on the same ID.