Node.js Operations
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.
The source candidate is aiwaf 1.1.5 with the declared aiwaf-wasm ^0.2.5 dependency. Publish the matching WASM companion first. The native request engine still requires explicit registration and awaits initialization; a missing native API fails startup. Legacy adapters keep their separate configuration and execution path.
Offline training now uses WASM receipts to prevent handled historical requests from extending or recreating blocks after expiry or manual removal. Models still fit on history; enforcement uses fresh requests. Preserve node_training_evidence.json in the blocklist data directory, or set AIWAF_TRAINING_STATE_DIR to a durable shared directory. Invalid timestamps are excluded. Missing WASM APIs, corrupt receipts, and storage failures make training fail visibly. A crashed trainer can leave node_training_evidence.lock; confirm it has stopped before removing that lock directory. Block and receipt writes are separate, so a crash between them can require recovery.
The current reference tests use Node 22.18.0, Express 4, Fastify 5, and NestJS 11. These are tested configurations, not a claim that every framework version is supported. Use the framework and adapter matching your application. Next.js examples require Node API request/response objects; Edge middleware is not supported by this adapter.
Legacy blocklist request checks and listing are read-only. Explicit database cleanup checks expiry in the DELETE statement; CSV cleanup reads current rows under the same cross-process lock used by blocklist writes. A crash can leave <blocklist CSV path>.lock; confirm its writer has stopped before removing that directory. Lock acquisition fails after 60 seconds. Restart all old writers when upgrading. These fixes do not make the explicit native engine's per-worker state distributed.
For source development, use the committed lockfile with npm ci, run the coverage and real native checks, and audit the complete tree with npm audit. Read dependency versions and verification and native migration.
How AIWAF works: system architecture
Legacy CLI and training from source
The commands in this section operate on legacy Node stores. They do not import or manage the native engine's dedicated CSV event files.
cd js
AIWAF_ACCESS_LOG=/path/to/access.log npm run train
npm run aiwaf -- status
npm run aiwaf -- list blocklist
npm run aiwaf -- add blocklist 203.0.113.9 "manual block"
npm run aiwaf -- add ip-exemption 203.0.113.10 "trusted monitor"
npm run aiwaf -- geo summary
npm run aiwaf -- model info
npm run aiwaf -- export aiwaf-export.json
Use Redis or another shared cache for consistent rate limiting across multiple Node processes. Without shared state, the in-memory fallback is process-local.
The npm run commands above use the package scripts in the monorepo's js/ directory. In an application that installed the package, use the installed binary, for example npx aiwaf status or npx aiwaf --help.
Configuration
| Area | Important options |
|---|---|
| Middleware | AIWAF_MIDDLEWARES, AIWAF_DISABLE_MIDDLEWARES, AIWAF_PATH_RULES |
| Rate limiting | WINDOW_SEC, MAX_REQ, FLOOD_REQ, cache |
| Headers | AIWAF_HEADER_VALIDATION, AIWAF_REQUIRED_HEADERS, AIWAF_HEADER_QUALITY_MIN_SCORE |
| Methods | AIWAF_METHOD_POLICY_ENABLED, AIWAF_ALLOWED_METHODS |
| Keywords | staticKeywords, AIWAF_ENABLE_KEYWORD_LEARNING, AIWAF_DYNAMIC_TOP_N |
| Honeypot | HONEYPOT_FIELD, timing settings |
| GeoIP | AIWAF_GEO_BLOCK_ENABLED, AIWAF_GEO_BLOCK_COUNTRIES, AIWAF_GEO_ALLOW_COUNTRIES |
| Models | AIWAF_MODEL_STORAGE, AIWAF_MODEL_PATH, AIWAF_MODEL_CACHE_KEY |
| Logging | AIWAF_MIDDLEWARE_LOGGING, AIWAF_MIDDLEWARE_LOG_PATH, DB/CSV options |
| WASM | AIWAF_WASM_VALIDATION, AIWAF_WASM_VALIDATE_RECENT |
AIWAF loads aiwaf-wasm when available and falls back to the JavaScript implementation when WASM initialization or conversion is not worthwhile. Small, object-heavy operations can be faster in JavaScript because crossing the JS/WASM boundary and serializing objects has a fixed cost. WASM is most useful for sufficiently large feature batches and model scoring workloads.
Development checks
Run from the monorepo root:
npm ci --prefix js
npm test --prefix js
python aiwaf_test.py --js-only
python aiwaf_test.py --wasm-only
Packaging
The package manifest is js/package.json; the CLI entry is js/bin/aiwaf.js. Release tags use js-v<version> and the npm-publish.yml workflow. The root README and LICENSE are staged into package artifacts by release automation.
Request parsing and cache failures
For custom Node HTTP servers or middleware registered before a framework parser, call aiwaf.bodyParser() before AIWAF and before forwarding requests. It parses JSON and URL-encoded bodies, retains original forwarding bytes, and rejects oversized bodies (default 64 KiB). Fastify/Hapi integrations must enforce AIWAF after body parsing and before their proxy handler.
Redis rate-cache failures return generic HTTP 503. AIWAF_RATE_CACHE_FAILURE_MODE defaults to 'closed'; explicitly choosing 'open' allows requests during cache failures. The new filesystem CSV transactions and retained training handles are Python/PyO3 capabilities; installing WASM does not migrate Node middleware/storage to that implementation.
Native deployment and benchmarking
Native mode: Node source candidate aiwaf 1.1.5 declares aiwaf-wasm ^0.2.5. Explicit native registration validates NativeRequestPipeline at startup; missing or incompatible support is an error. There is no JavaScript policy fallback for this engine. Installing the dependency does not switch an existing legacy middleware chain.
Build/install the matching WASM companion first, await initialization, verify diagnostics().native and diagnostics().model, and test normal routes, unknown PHP paths and nested SQL bodies. Use setModel(model) to replace the fitted Rust snapshot or restart. The legacy trainer/log stores are separate from the native event files; do not assume an automatic import.
Run npm run test:native from js/ in the source checkout. node scripts/benchmark_js_pipeline.js compares full HTTP requests against bare Express, with AI off/on and actual inference counters. Server samples include synchronous finalization/logging, and overhead subtracts the framework-only baseline. Legacy SQLite/JSONL and native Rust/CSV have different persistence semantics, so comparisons are integration results rather than pure language speedups.
Stop traffic and wait for in-flight responses before close(). Model/rate/honeypot/history state is local; persisted blocks/keywords only reload on reconstruction. Publication must make the compatible WASM API available before users enable native mode.
Read the native lifecycle, configuration, deployment and measurements, and migration.
Measured Node request overhead
Windows x64, Node v22.18.0, Express 4.22.1, built WASM 0.2.2. Three alternating runs of 300 requests per mode, one keep-alive client. 80% GET / 20% JSON POST with 30 fields; 1,000 blocklist entries; all eight stages selected; memory rate/form state. Native AI uses a seeded 100-tree forest fitted to 200 rows, with one inference per measured/warm-up request verified by counters.
| Mode | Server processing | Added overhead |
|---|---|---|
| Express only | 0.37 ms | 0.00 ms |
| Legacy, AI off | 3.86 ms | 3.48 ms |
| Native, AI off | 0.60 ms | 0.22 ms |
| Native, AI on | 0.64 ms | 0.27 ms |
Server samples include synchronous finalization and immediate logging. Overhead subtracts the matched bare Express baseline; figures are medians of per-run means. Legacy uses SQLite policy lookups and one immediate JSONL append; native uses Rust state and one immediate CSV append. These stores/policies have different persistence semantics, so this is an integration comparison, not a pure Rust-versus-JavaScript equivalence test. GeoIP is an unknown-country fixture, not a real database lookup. Synthetic local latency does not establish production throughput or latency.
Native execution and actual inference counters were verified; all 3,600 measured requests returned 200. Protected SQL/PHP probes returned 403. Reproduce from the source checkout with scripts/benchmark_js_pipeline.js.
Dependency security and reproducible checks
The source lockfile uses Jest 30.5.2, Fastify 5.12.7, and patched NestJS 11 dependencies (the validated install resolved 11.2.7). The direct glob dependency is 13.0.6. Targeted overrides update qs and the NYC loader's YAML dependency; old global parser overrides have been removed. Framework packages in devDependencies support adapter testing; they do not select the framework for an installed application.
cd js
npm ci
npm audit
npm run test:coverage
npm run test:wasm
npm run test:native
The repository's Jest tests use generated CommonJS WASM bindings through AIWAF_NATIVE_TEST_MODULE. Release CI builds that fixture from rust/crates/aiwaf_wasm and runs real installed-WASM checks separately. Jest also transforms the ESM cookie dependency used by Fastify's injection client so the suite works on Node 22.
Earlier dependency validation on 13 October 2026 passed 227 tests across 63 suites, with coverage thresholds met, plus real WASM and native framework transport checks. npm ci and the full npm audit reported zero known vulnerabilities for that lockfile. The release workflow audits production and development dependencies. Rerun the audit after dependency changes; this result describes the tested tree and advisory database at that time.
Upstream deprecation warnings remain for SQLite's prebuild-install and coverage tooling's glob 10 dependency. Deprecation notices and blocked optional install scripts are separate from audit findings. The repository permits SQLite's install script; the tested installation did not require enabling every optional postinstall script.
The latency figures above were measured before this dependency update. They have not been remeasured with the patched tree.
October 2026 release upgrade
The prepared source versions are Python 1.1.6, Rust/Python 0.2.5, Node.js 1.1.4, WASM 0.2.4, and Java 1.3.4. These are source targets; verify registry availability before installing. Python requires aiwaf-rust>=0.2.5, including the TrainingEvidence API. Node offline training requires the matching WASM receipt API.
- Back up policies, logs, models, and training evidence. Use the same absolute data and evidence directories for application workers and CLI trainers.
- Install the matching native companion before upgrading its host package: Rust before Python, WASM before Node. Preserve
training_evidence.jsonandnode_training_evidence.jsonacross expiry, unblocking, restarts, and log rotation. - Stop older file writers and restart all workers on the new code. Mixing old unlocked writers with new writers can still lose updates.
- In staging, train twice on identical logs, then verify handled evidence does not recreate a removed or expired block. Add genuinely fresh requests and confirm they must independently satisfy the enforcement threshold.
- Verify that listing and expiry checks preserve concurrently refreshed blocks and unrelated IPs. Check the raw expiry and backend location if entries disappear unexpectedly.
Java CSV and file storage
Java 1.3.4 reloads current storage under a shared JVM and OS sidecar lock for each operation. Reads filter expiry without rewriting disk; mutations preserve other instances' updates and report persistence failures. This adds file I/O. Java model training still does not add blocklist entries. Rate buckets and other per-engine memory state retain their existing isolation.
Lock recovery
Python training uses an OS file lock; Java CSV/file OS locks also release on process exit. Keep their stable sidecar lock files in place while workers run. Node uses lock directories for training and legacy CSV mutations; a crash can leave them behind. Confirm the owning process has stopped before removing a Node lock directory. Do not delete training receipts to clear a lock: doing so loses handled-evidence history.
Release checks
Local validation passed 247 Node tests across 67 suites with the coverage gate, 215 Java tests with 3 skips, and 38 Rust/WASM native tests. After the native dependency bump, 11 affected Python packaging tests passed through the appropriate Django and pytest runners. These checks do not replace full release CI, dependency audits, signing, or operating-system wheel builds. Build Linux/macOS Rust wheels using the release matrix; the local bundle contains a Windows x86-64 wheel.
For release maintainers: publish the WASM companion, refresh the Node lockfile from the registry to record its actual integrity, then run npm ci and the native integration checks. Do not substitute an older native dependency to satisfy a stale packaging test. See the changelog.
