Rust and WASM Architecture
Rust implements shared computation primitives. PyO3 and wasm-bindgen expose them to host applications, while the host owns framework hooks and responses. Python PyO3 bindings also provide native CSV storage, logging writes, and shared policy computations; the WASM filesystem boundary remains different.
Source and package versions
One AIWAF repository.
Python: py/aiwaf/ · Node.js: js/ · Rust/WASM: rust/ · Java: java/.
Source and canonical README.
Versions shown describe the source checkout; package releases are independent.
Current Python/Node source targets are aiwaf 1.1.8 (Python) and
aiwaf 1.1.5 (npm), with Rust/Python 0.2.7 and
WASM 0.2.5 and Java 1.3.5. Sandbox guides retain their separately pinned builds.
How AIWAF works: system architecture
One core, two binding boundaries
- Host runtimeSelect the operation and provide normalized input.
- Binding conversionPyO3 or wasm-bindgen converts host values into Rust data.
- Shared Rust coreMatch, validate, extract features, analyze behavior, or score a forest.
- Host decisionConvert the result back and apply framework policy.
| Source area | Responsibility |
|---|---|
rust/crates/aiwaf_core/src/lib.rs | Shared keyword and route matching, request records, feature windows, behavior analysis, and Isolation Forest state. |
rust/src/lib.rs | PyO3 objects and conversion routines exposed as aiwaf_rust. |
rust/crates/aiwaf_wasm/src/lib.rs | wasm-bindgen exports and JavaScript conversion exposed as aiwaf-wasm. |
Matching without rebuilding work per request
KeywordMatcher escapes configured keywords into literal patterns and builds a regex set. first_match preserves configured keyword order. The host can reuse the matcher rather than rebuilding pattern machinery on each request.
RouteMatcher indexes normalized prefixes. Matching starts with the deepest candidate and walks parent path boundaries until it finds a stored prefix. Duplicate normalized prefixes retain their first configured index. The host still normalizes paths and interprets the matching rule's policy.
Record conversion and stateful feature extraction
Bindings convert request records into typed structures before the core computes features. Batched and incremental extraction share the computation layer; incremental calls carry explicit state so recent client activity is available across batches.
That extraction state is distinct from a framework's blocklist or distributed rate-limit store. Sending a batch to Rust does not automatically replicate application state across workers. The host chooses where records and extraction state live.
| Feature | What it describes | Why it helps |
|---|---|---|
path_len | Length of the request path. | Unusually long probing paths. |
kw_hits | Matches against malicious keywords. | Known probing patterns within a path. |
resp_time | Response duration in seconds. | Behavior that differs from the normal timing distribution. |
status_idx | An encoded response-status category. | Response outcomes without treating status codes as continuous measurements. |
burst_count | Requests associated with the client in the burst window. | Rapid repeated activity. |
total_404 | Not-found responses in the relevant per-client history. | Repeated discovery of nonexistent routes. |
The feature order is part of the model contract. A trainer and scorer must agree on the order, status mapping, time units, and window semantics. Summary fields ending in _ms use milliseconds; the feature vector uses seconds. Sharing feature names alone does not make every model artifact interchangeable.
Inside the forest
Training samples rows and grows isolation trees with random feature selections and split points. Tree traversal measures the number of splits required to isolate a point; short average isolation paths indicate more unusual samples.
anomaly_score uses the direction where higher means more anomalous. score_samples uses the opposite, scikit-learn-aligned direction. decision_function subtracts the configured offset, and predict returns inlier or outlier labels. The host must use the matching threshold convention.
Ordinary fitting replaces the forest unless warm-start append is selected. Retraining supports the append lifecycle. Random seeds and sampling settings affect the trained state, so score comparisons should use compatible artifacts and settings.
JSON is the artifact boundary
The forest exports state through to_json and restores it through from_json. Tree structure, settings, and scoring state travel as data. The host decides whether to store that state in a file, database, or cache and checks feature and artifact compatibility when loading.
Python's Rust-model artifact can be exchanged with the supported Java JSON model path. JavaScript-native forests and older artifacts are not automatically the same format just because they use six feature names.
Acceleration has a conversion cost
Python calls cross the native-extension boundary; JavaScript calls cross the WASM boundary. Converting object-heavy inputs consumes time before the Rust computation starts. Reusing matchers and processing sufficiently large feature batches can amortize that cost.
The legacy Python bridge selects capabilities and retains fallback behavior. The explicit NativeCSVPipeline validates its API once and has no Python policy fallback. The Node adapter can choose JavaScript when WASM initialization or conversion is unsuitable. A native function returning a suspicious reason still requires the host to turn that result into a policy decision.
Worked example: accelerated header validation
The Python adapter receives framework headers and normalizes HTTP or WSGI key names. The bridge passes the resulting dictionary and validation options through PyO3. Rust evaluates the configured header rules and returns a reason or a clean result. The Python middleware then decides whether to return a response, record an offense, or continue to the handler.
The Rust core never sends the Flask or FastAPI response itself. This boundary explains why framework configuration and exemptions continue to matter when native acceleration is enabled.
Trace the implementation
Follow these files in the monorepo to connect the architecture to the code:
Native CSV request engine and training
Release candidate: this page documents Python 1.1.8 and Rust/Python 0.2.7 in the current source checkout. Source versions do not establish registry availability. Python 1.1.8 requires aiwaf-rust>=0.2.7; framework dependencies remain extras.
Rust owns the native CSV pipeline configuration, enforcement order, SQL and keyword inspection, geo/blocklist decisions, rate buckets, form timing, UUID scores, anomaly history/inference, keyword learning, and CSV logging. Python supplies framework routes, authentication facts, GeoIP lookup, bounded request buffering, and response construction. Training and management still have Python interfaces.
PyO3 exposes NativeCSVPipeline and opaque NativePipelineRequest, alongside locked CSV transactions, snapshots, cached append writers, TrainingCSVReader, NativeTrainingBatch and NativeTrainingBuilder. Native ISO parsing remains optional in compatibility training paths.
WASM has a separate API: it does not expose this filesystem request pipeline or retained Python training objects.
Continue with the request lifecycle, the native profile, migration, and performance and deployment.
