Configuration Reference
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.
Configuration is adapter-specific. Use the names accepted by your runtime, and apply settings before registering protection. The tables below describe source defaults; they do not guarantee that a middleware is selected or that a route is protected.
Inputs and precedence
| Runtime | Input | Resolution |
|---|---|---|
| Flask | Flat app.config['AIWAF_*'] settings; constructor middleware selection | Load application configuration before AIWAF(app). A shell variable does not automatically become Flask app.config. The middleware reads adapter settings and its own defaults. |
| Django | Flat settings in settings.py | Explicit flat settings take precedence over legacy AIWAF_SETTINGS compatibility mappings for settings that use set-if-missing. Django does not automatically read arbitrary environment variables; map them in settings.py. |
| FastAPI | Nested constructor options or AIWAFConfig | Defaults, then JSON file, then recognized environment mappings, then explicit constructor overrides. If a config object is supplied, it is used directly rather than rebuilding from config_file/overrides. |
| Node.js | Options to the framework adapter | The normalized resolver uses the first defined canonical option, supported aliases, legacy settings, recognized environment variables, and finally defaults. false and 0 remain explicit values. |
| Java/Spring | AiwafConfig, nested JSON, environment, Spring properties | Spring's loader creates defaults/file configuration, applies supported environment mappings, then Spring property lookups. Custom configuration beans replace auto-configuration where supported. |
| Rust/WASM | Arguments to primitive bindings | These bindings are not a framework configuration loader. Host adapters control middleware, policy, storage, and proxy handling. |
Flask and Django flat settings
The nested Python table below is the shared configuration manager, primarily exposed through FastAPI. It is not the Flask/Django constructor signature. Their important flat names include AIWAF_RATE_WINDOW (seconds), AIWAF_RATE_MAX and AIWAF_RATE_FLOOD (integer counts), AIWAF_EXEMPT_PATHS (path list), AIWAF_PATH_RULES (rule list), AIWAF_GEO_BLOCK_COUNTRIES and AIWAF_GEO_ALLOW_COUNTRIES (country-code lists), and AIWAF_MODEL_PATH (path). Consult each adapter's settings guide for the full flat surface.
# Flask: set values before middleware registration.
app.config.update(
AIWAF_RATE_WINDOW=10,
AIWAF_RATE_MAX=20,
AIWAF_RATE_FLOOD=40,
AIWAF_RATE_CACHE_BACKEND='redis',
AIWAF_REDIS_URL='redis://localhost:6379/0',
AIWAF_RATE_CACHE_KEY_PREFIX='my-service:rate:',
)
AIWAF(app, middlewares=['all'])
Flask's rate middleware defaults to 10 seconds, 20 requests, and flood threshold 40. The website overrides these values in app.py. Node's normalized defaults are 60 seconds, 100 requests, and flood threshold 200. Do not use website thresholds as universal package defaults.
FastAPI nested options
from fastapi import FastAPI
from aiwaf.fast import AIWAF
app = FastAPI()
AIWAF(app, middlewares=['all'],
rate_limiting={'window_seconds': 10, 'max_requests': 20,
'flood_threshold': 40, 'cache_backend': 'redis',
'redis_url': 'redis://localhost:6379/0',
'cache_key_prefix': 'my-service:rate:'})
The shared environment loader recognizes names such as AIWAF_RATE_WINDOW_SECONDS and AIWAF_RATE_MAX_REQUESTS. Those differ from the Flask/Django flat names. Boolean strings accept true, 1, yes, and on as true; list mappings split comma-separated values. File lists use JSON arrays. Review the environment map before assuming a setting is supported.
Middleware, route policy, and exemptions
all selects the canonical middleware set. auto omits checks based on detected application signals. Explicitly disabled middleware and route exemptions can bypass checks even when their configuration says enabled. Regenerate route manifests after route changes and inspect the selected middleware at runtime.
Default exemptions differ. Django includes administrative/login paths; Flask includes health, crawler, static and asset paths. Private-address behavior also differs from an explicit trusted-client allowlist. Review Python default exemption lists and the policy lifecycle before interpreting a successful local request as a public-client test.
Configure forwarding-header handling with the actual deployment topology. Java exposes trustedProxyCidrs and maxForwardedForEntries. Python and Node extraction use adapter-specific rules; these Java options are not portable knobs. Have the outer proxy overwrite untrusted client forwarding headers, and compare observed client addresses with the intended public client.
Multiple workers and storage
Persistent blocklist files, model storage, logs, and rate counters have separate ownership. A CSV blocklist does not share every in-memory counter. Flask/FastAPI use separate rate-cache settings; install the Redis client required by the selected backend. Flask rejects invalid Redis configuration at initialization. Python rate-cache failures produce generic HTTP 503 responses, and FastAPI does not silently select a process-local cache. Verify repeated requests across workers and inspect backend diagnostics.
Node accepts a shared cache implementation; its fallback is process-local. Java Redis shares enforcement state within storageKeyPrefix, supports standalone mode, and defaults to fail_closed. Use the same namespace only for intentionally shared policy/state, and review schema compatibility before mixed-version deployments.
Source default tables
The Python table includes every leaf returned by the shared manager. The Node table includes normalized options, not every raw option consumed by individual modules (for example cache/WASM settings). The Java table includes public AiwafConfig field initializers. Disabled middleware can leave a setting inactive. These tables are a source snapshot, not your effective live configuration.
Source snapshot: Python 1.1.3, Node.js 1.1.2, Java 1.3.3; commit 517bb28c6f7fa51f098547d9efb94035108bd50a. These are legacy configuration defaults, not the separate lowercase native pipeline profile or a registry availability check. Runtime configuration and route policies can change effective values.
Python nested configuration defaults
| Setting | Type | Source default |
|---|---|---|
storage.backend | str | "memory" |
storage.file_path | str | "aiwaf_data.json" |
header_validation.enabled | bool | true |
header_validation.block_suspicious | bool | true |
header_validation.quality_threshold | int | 3 |
header_validation.exempt_paths | list | ["/health", "/healthz", "/status", "/ping", "/metrics", "/favicon.ico", "/robots.txt"] |
header_validation.custom_suspicious_patterns | list | [] |
header_validation.custom_legitimate_patterns | list | [] |
header_validation.trust_legitimate_bots | bool | false |
rate_limiting.enabled | bool | true |
rate_limiting.max_requests | int | 20 |
rate_limiting.window_seconds | int | 10 |
rate_limiting.flood_threshold | int | 40 |
rate_limiting.key_mode | str | "ip_path" |
rate_limiting.soft_block_blocklist | bool | false |
rate_limiting.cache_backend | str | "memory" |
rate_limiting.redis_url | null or string | null |
rate_limiting.cache_key_prefix | str | "aiwaf:rate:" |
rate_limiting.exempt_ips | list | [] |
ip_keyword_block.enabled | bool | true |
ip_keyword_block.sql_injection_mode | str | "block" |
ip_keyword_block.payload_max_bytes | int | 65536 |
ip_keyword_block.malicious_keywords | list | [".php", "xmlrpc", "wp-", ".env", ".git", ".bak", "shell", "filemanager"] |
honeypot.enabled | bool | true |
honeypot.min_form_time | float | 1.0 |
honeypot.max_page_time | int | 240 |
geo_block.enabled | bool | false |
geo_block.allow_countries | list | [] |
geo_block.block_countries | list | [] |
ai_anomaly.enabled | bool | true |
uuid_tamper.enabled | bool | true |
logging_middleware.enabled | bool | true |
logging_middleware.log_dir | str | "aiwaf_logs" |
logging_middleware.log_format | str | "combined" |
blocklist.default_block_duration | int | 3600 |
blocklist.permanent_block_threshold | int | 5 |
blocklist.auto_unblock_enabled | bool | true |
blocklist.cleanup_interval | int | 3600 |
exemptions.private_ips_exempted | bool | true |
exemptions.localhost_exempted | bool | true |
exemptions.auto_exempt_patterns | list | ["127.0.0.1", "::1", "192.168.*.*", "10.*.*.*", "172.16.*.*"] |
security.log_blocked_requests | bool | true |
security.log_suspicious_requests | bool | true |
security.max_header_length | int | 8192 |
security.max_user_agent_length | int | 512 |
performance.enable_caching | bool | true |
performance.cache_duration | int | 300 |
performance.max_cache_entries | int | 10000 |
logging.level | str | "INFO" |
logging.format | str | "%(asctime)s - %(name)s - %(levelname)s - %(message)s" |
logging.log_file | null or string | null |
path_rules | list | [] |
Node.js normalized option defaults
| Setting | Type | Source default |
|---|---|---|
staticKeywords | array | [] |
dynamicTopN | number | 10 |
WINDOW_SEC | number | 60 |
MAX_REQ | number | 100 |
FLOOD_REQ | number | 200 |
HONEYPOT_FIELD | optional | unset (resolved by consumer) |
AIWAF_MIN_FORM_TIME | number | 0 |
AIWAF_MAX_PAGE_TIME | number | 3600 |
AIWAF_LOGIN_PATH_PREFIXES | array | [] |
AIWAF_POST_ONLY_SUFFIXES | array | [] |
AIWAF_ALLOWED_METHODS | array | [] |
AIWAF_METHOD_POLICY_ENABLED | boolean | false |
AIWAF_EXEMPT_PATHS | array | [] |
AIWAF_EXEMPT_IPS | array | [] |
AIWAF_ALLOWED_PATH_KEYWORDS | array | [] |
AIWAF_EXEMPT_KEYWORDS | array | [] |
AIWAF_EXEMPTIONS_DB | boolean | true |
AIWAF_HEADER_VALIDATION | boolean | false |
AIWAF_REQUIRED_HEADERS | array | [] |
AIWAF_HEADER_QUALITY_MIN_SCORE | number | 3 |
AIWAF_MAX_HEADER_BYTES | number | 32768 |
AIWAF_MAX_HEADER_COUNT | number | 100 |
AIWAF_MAX_USER_AGENT_LENGTH | number | 500 |
AIWAF_MAX_ACCEPT_LENGTH | number | 4096 |
AIWAF_BLOCKED_USER_AGENTS | array | ["sqlmap","nikto","acunetix","nmap","masscan"] |
AIWAF_GEO_BLOCK_ENABLED | boolean | false |
AIWAF_GEO_BLOCK_COUNTRIES | array | [] |
AIWAF_GEO_ALLOW_COUNTRIES | array | [] |
AIWAF_GEO_MMDB_PATH | optional | unset (resolved by consumer) |
AIWAF_GEO_CACHE_SECONDS | number | 3600 |
AIWAF_GEO_CACHE_PREFIX | string | "aiwaf:geo:" |
AIWAF_DYNAMIC_KEYWORDS_DB | boolean | true |
AIWAF_MODEL_STORAGE | string | "file" |
AIWAF_MODEL_PATH | optional | unset (resolved by consumer) |
AIWAF_MODEL_STORAGE_FALLBACK | string | "file" |
AIWAF_MODEL_CACHE_KEY | optional | unset (resolved by consumer) |
AIWAF_MODEL_CACHE_TTL | number | 0 |
AIWAF_MIDDLEWARE_LOGGING | boolean | false |
AIWAF_MIDDLEWARE_LOG_PATH | string | "logs/aiwaf-requests.jsonl" |
AIWAF_MIDDLEWARE_LOG_DB | boolean | false |
AIWAF_MIDDLEWARE_LOG_CSV | boolean | false |
AIWAF_MIDDLEWARE_LOG_CSV_PATH | string | "logs/aiwaf-requests.csv" |
AIWAF_CAPTURE_EXTENDED_REQUEST_INFO | boolean | false |
AIWAF_EXTENDED_INFO_MAX_HEADERS | number | 50 |
AIWAF_EXTENDED_INFO_MAX_VALUE_LENGTH | number | 512 |
AIWAF_MIN_AI_LOGS | number | 0 |
AIWAF_ENABLE_KEYWORD_LEARNING | boolean | true |
AIWAF_MIDDLEWARES | optional | unset (resolved by consumer) |
AIWAF_DISABLE_MIDDLEWARES | array | [] |
AIWAF_PATH_RULES | array | [] |
AIWAF_PATH_MANIFEST | string | ".aiwaf/paths.json" |
AIWAF_ROUTE_PLAN_VERSION | number | 0 |
AIWAF_ACCESS_LOG | optional | unset (resolved by consumer) |
AIWAF_RATE_CACHE_FAILURE_MODE | string | "closed" |
AIWAF_FORCE_JSON_ERRORS | boolean | true |
Java AiwafConfig field defaults
Java expressions are shown as declared in source. Collection constructors describe initial values; constants are resolved by the Java implementation.
| Setting | Type | Source default |
|---|---|---|
headerValidationEnabled | boolean | true |
rateLimitEnabled | boolean | true |
rateLimitMax | int | 20 |
rateLimitWindowSeconds | int | 10 |
rateLimitScope | RateLimitScope | RateLimitScope.PER_PATH |
blockIpOnRateLimitBreach | boolean | false |
blockIpOnFloodBreach | boolean | false |
rateLimitFloodThreshold | int | 40 |
trustedProxyCidrs | Set<String> | new HashSet<>(Set.of("127.0.0.0/8", "::1/128")) |
maxForwardedForEntries | int | 16 |
exemptIps | Set<String> | new HashSet<>() |
exemptIpPatterns | Set<String> | new HashSet<>() |
privateIpsExempted | boolean | true |
localhostExempted | boolean | false |
autoExemptPathPrefixes | Set<String> | new HashSet<>() |
exemptPaths | Set<String> | new HashSet<>(Arrays.asList( "/favicon.ico", "/robots.txt", "/sitemap.xml", "/sitemap.txt", "/ads.txt", "/security.txt", "/.well-known/", "/apple-touch-icon.png", "/apple-touch-icon-precomposed.png", "/manifest.json", "/browserconfig.xml", "/health", "/healthcheck", "/ping", "/status", "*.css", "*.js", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.ico", "*.svg", "*.woff", "*.woff2", "*.ttf", "*.eot", "/static/", "/assets/", "/css/", "/js/", "/images/", "/img/", "/fonts/", "/healthz", "/metrics", "/api/health", "/api/status" )) |
exemptAllowWildcards | boolean | true |
exemptAllowPrefix | boolean | true |
geoBlockEnabled | boolean | false |
geoAllowedCountries | Set<String> | new HashSet<>() |
geoBlockedCountries | Set<String> | new HashSet<>() |
geoExemptPaths | Set<String> | new HashSet<>() |
geoIpDatabasePath | String | null |
geoCacheSeconds | int | 3600 |
geoMaxCacheEntries | int | 10_000 |
honeypotEnabled | boolean | true |
minFormTimeSeconds | double | 1.0 |
maxFormPageTimeSeconds | double | 240.0 |
loginMinFormTimeSeconds | double | 0.1 |
loginPathPrefixes | Set<String> | new HashSet<>(Set.of( "/admin/login/", "/login/", "/accounts/login/", "/auth/login/", "/signin/" )) |
uuidTamperEnabled | boolean | true |
uuidScoreEnabled | boolean | true |
uuidScoreWindowSeconds | int | 60 |
uuidScoreBlockThreshold | int | 5 |
uuidMalformedWeight | int | 5 |
uuidNotFoundWeight | int | 1 |
uuidSuccessDecay | int | 2 |
uuidParameterNames | Set<String> | new HashSet<>(Set.of("uuid")) |
ipKeywordBlockEnabled | boolean | true |
methodValidationEnabled | boolean | true |
allowedMethods | Set<String> | new HashSet<>(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD")) |
enabledMiddlewares | Set<String> | new HashSet<>() |
disabledMiddlewares | Set<String> | new HashSet<>() |
maxHeaderBytes | int | 32 * 1024 |
maxHeaderCount | int | 100 |
maxUserAgentLength | int | 500 |
maxAcceptLength | int | 4096 |
maxRequestBodyBytes | int | 1024 * 1024 |
requestBodyInspectionBytes | int | 64 * 1024 |
requestBodyInspectionEnabled | boolean | true |
sqlInjectionMode | String | "block" |
allowCompressedRequestBodies | boolean | false |
maxRuntimeStateEntries | int | 10_000 |
maxParameterCount | int | 200 |
maxParameterBytes | int | 16 * 1024 |
allowDuplicateParameters | boolean | false |
allowedContentTypesByPathPrefix | Map<String, Set<String>> | new HashMap<>() |
minHeaderQualityScore | int | 3 |
requiredHeaders | List<String> | new ArrayList<>(HeaderValidationCore.REQUIRED_HEADERS) |
requiredHeadersByMethod | Map<String, List<String>> | new HashMap<>() |
blockedPathPatterns | Set<String> | new HashSet<>(Arrays.asList(".env", "../", "%2e%2e", "wp-admin", "phpmyadmin", ".git")) |
pathRules | List<PathRule> | new ArrayList<>() |
pathManifestEnabled | boolean | true |
pathManifestPath | String | ".aiwaf/paths.json" |
aiEnabled | boolean | true |
aiLazyLoadModel | boolean | true |
aiBackgroundPreload | boolean | false |
aiModelPath | String | "aiwaf-model.bin" |
aiAnomalyScoreThreshold | double | 0.7 |
aiRequireBehaviorConfirmation | boolean | true |
aiRecentWindowSeconds | int | 300 |
aiMinRecentSamplesToBlock | int | 5 |
aiTimingLogsEnabled | boolean | false |
enableKeywordLearning | boolean | true |
dynamicTopN | int | 10 |
exemptKeywords | Set<String> | new HashSet<>() |
legitimatePathKeywords | Set<String> | new HashSet<>() |
storeExtendedBlockInfo | boolean | true |
blockInfoMaxHeaders | int | 50 |
blockInfoMaxHeaderValueLength | int | 512 |
blockInfoRedactHeaders | Set<String> | new HashSet<>(Set.of("authorization", "cookie", "set-cookie")) |
sensitiveParameterNames | Set<String> | new HashSet<>(Set.of( "password", "passwd", "pwd", "token", "access_token", "refresh_token", "secret", "api_key", "apikey", "authorization", "code", "session", "sessionid" )) |
logQueryParameters | boolean | false |
observabilityEnabled | boolean | true |
loggingEnabled | boolean | true |
logDir | String | "aiwaf_logs" |
logFormat | String | "combined" |
storageBackend | String | "memory" |
storageFilePath | String | null |
storageRedisUrl | String | null |
storageKeyPrefix | String | "aiwaf:" |
storageRedisMode | String | "standalone" |
storageRedisFailureMode | String | "fail_closed" |
storageRedisConnectionTimeoutMillis | int | 1_000 |
storageRedisSocketTimeoutMillis | int | 2_000 |
storageRedisPoolMaxTotal | int | 32 |
storageRedisPoolMaxIdle | int | 16 |
storageRedisPoolMinIdle | int | 0 |
storageRedisPoolMaxWaitMillis | int | 250 |
legitimateRouteHints | Set<String> | new HashSet<>() |
Source loaders and complete adapter references
- Python nested defaults and environment mappings; FastAPI constructor precedence.
- Flask flat settings guide; Django compatibility mapper; Python settings guide.
- Node options and alias resolution; Node middleware consumers.
- Java typed fields; Spring properties; Java environment mappings.
Legacy adapter native CSV settings
These uppercase settings apply to the retained legacy adapters. The explicit native pipeline uses a separate lowercase profile.
| Setting | Default and scope |
|---|---|
AIWAF_USE_RUST, AIWAF_RUST_CSV | Enabled by default for supported Flask/Django native CSV dispatch. Set either application setting to False to disable it. FastAPI training also honors these application options. |
AIWAF_RUST_CSV=false environment variable | Disables shared native dispatch outside Flask/Django application contexts. Configured framework settings take precedence over this environment fallback. |
AIWAF_CSV_REQUEST_CACHE | Flask defaults to True. Reuses exemptions within a request while rechecking file metadata/configuration; does not cache rate mutations or blocklist expiry decisions. |
AIWAF_CSV_LOG_FSYNC | Flask CSV logging defaults to False. True synchronizes each row to disk. Changed policy-state writes still synchronize. |
AIWAF_RUST_ISO_DATES=1 | Optional environment flag for native ISO timestamp parsing. Python's faster C ISO parser remains the default. |
AIWAF_RATE_CACHE_BACKEND='csv' | Explicit Flask selection of persistent shared rate buckets. Memory remains the default. Requires native support; unavailable/disabled native support returns HTTP 503. |
AIWAF_RATE_CSV_PATH | Optional Flask path; defaults to rate_buckets.csv under AIWAF_DATA_DIR. |
# Set before AIWAF(app).
app.config.update(AIWAF_USE_CSV=True, AIWAF_USE_RUST=True,
AIWAF_RUST_CSV=True, AIWAF_CSV_LOG_FSYNC=False)
# Optional, costlier than process memory: shared CSV rate counters.
app.config['AIWAF_RATE_CACHE_BACKEND'] = 'csv'
CSV buckets trim the request window, consume a request, evaluate thresholds, and persist TTL under one native transaction. Rewriting and syncing on every request can cost more than Redis or memory. A CSV blocklist alone does not select this rate backend.
Native pipeline profile (Python 1.1.8)
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.
Pass a dedicated lowercase config dictionary in Flask/FastAPI, or use AIWAF_CSV_PIPELINE in Django. Existing uppercase settings, nested AIWAFConfig dictionaries, PATH_RULES, manifests and custom policy callbacks are not merged into this profile. Unknown keys fail initialization. The profile is immutable; construct a new engine and reload workers to change it. Legacy Rust-disable flags do not select a Python fallback for this engine.
| Profile key | Default and meaning |
|---|---|
enabled | All eight stage names; explicitly select a subset if needed |
data_dir | aiwaf_data; native policy CSV directory |
exempt_paths / exempt_ips | /health, /robots.txt, /sitemap.xml; no IPs. Literal case-insensitive path prefixes |
sql_mode / max_bytes | block / 65536 bytes; off supported, monitor rejected; bounded structured traversal |
learning / top_n | true / 10; sorted stable presence-keyword subset |
geo_allow / geo_block | Empty arrays plus stored blocked countries; unknown country allowed |
rate_backend / rate_key_mode | memory / ip_path; csv shares buckets, ip aggregates paths; Redis rejected |
rate_window / rate_max / rate_flood | 10 seconds / 20 / 40; throttle 429, flood 403 with blocklist persistence |
honeypot_backend | Django defaults to django_cache via CACHES; other native adapters default to csv (shared/persistent). memory is per engine/worker and resets on restart. Django accepts honeypot_cache_alias (default: default) |
min_form_time / max_page_time / skip_authenticated | 1.0 seconds / 240 seconds / true |
uuid_score_enabled / uuid_window / uuid_threshold | true / 60 seconds / 5; malformed weight 5, not-found weight 1, success decay 2 |
required_headers / header_min_score | HTTP_USER_AGENT and HTTP_ACCEPT / 3; empty required list retains safety caps |
anomaly_window | 60 seconds; history and loaded model inference are native and per worker |
log_path / log_fsync | data_dir/requests.csv / false; immediate append, optional per-row fsync |
config = {
"data_dir": "aiwaf_data",
"rate_backend": "memory",
"honeypot_backend": "csv",
"log_fsync": False,
}Keep CSV honeypot timing when workers need shared persistent timestamps. Choose memory explicitly only when local timing and reset on restart fit the deployment. The full source profile also includes keyword vocabulary, blocklist behavior and UUID weights; see rust/src/csv_pipeline.rs.
Continue with the request lifecycle, the native profile, migration, and performance and deployment.
Node native pipeline profile
Native mode: Node source candidate aiwaf 1.1.4 declares aiwaf-wasm ^0.2.4. 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.
Adapter options: profile, dataDir, logPath, logFsync, persistBlocks, model, synchronous countryLookup/authenticated/routeExists, and onError. Unknown options fail startup. The profile is immutable and accepts lowercase keys only; reconstruct/reload to change it.
| Profile keys | Defaults and meaning |
|---|---|
| enabled | All eight stages |
| exempt_paths / exempt_ips | /health, /robots.txt, /sitemap.xml; empty IPs; literal case-insensitive prefixes |
| sql_mode / max_bytes | block / 65536; off supported, monitor rejected |
| rate_window / rate_max / rate_flood / rate_key_mode | 10 seconds / 20 / 40 / ip_path (ip also supported); local Rust counters |
| min_form_time / max_page_time / skip_authenticated / honeypot_field | 1.0 seconds / 240 seconds / true / empty field; memory timing per engine |
| geo_allow / geo_block / blocklist | Empty; blocklist IP maps to Unix expiry seconds, 0 permanent. Unknown country allowed |
| static_keywords / dynamic_keywords / legitimate_keywords / learning | Native vocabulary / empty / exact legitimate route names / true; bounded probe learning |
| allowed_methods / uuid_prefix / uuid_window / uuid_threshold | No restriction / /user/ / 60 seconds / 5; malformed UUID rejected |
| required_headers / header_min_score | HTTP_USER_AGENT and HTTP_ACCEPT / 3; safety caps retained |
| anomaly_window / max_history / max_clients / max_pending | 60 seconds / 1000 / 100000 / 10000; bounded local state |
| block_duration | 86400 seconds for new blocks; separate from legacy reputation escalation |
logFsync defaults to false; logging remains immediate. persistBlocks defaults to true and syncs block/keyword event rows immediately, replaying them at startup. This does not provide live cross-worker sharing; use separate files per worker. Legacy Redis/ORM/custom callbacks, manifests and uppercase settings are not imported.
Read the native lifecycle, configuration, deployment and measurements, and migration.
Shared Python CSV storage
List APIs and command help now use blocklist and allowlist. Earlier import/method/command spellings remain compatibility aliases. Existing CSV filenames and Django model identity are retained so upgrades preserve stored blocks and exemptions; use the package storage constants rather than constructing filenames.
Python 1.1.4 standardizes Flask, FastAPI, Django CSV mode, and the explicit native pipeline on blacklist.csv, whitelist.csv, keywords.csv, geo_blocked_countries.csv, and path_exemptions.csv. Keyword frequencies and non-policy runtime history are retained in runtime_metadata.csv. Memory, JSON-file, database, and Django ORM modes keep their existing storage behavior.
| Integration | CSV directory configuration |
|---|---|
| Legacy Flask | Set app.config['AIWAF_USE_CSV'] = True and app.config['AIWAF_DATA_DIR'] before initializing AIWAF. |
| Legacy Django | Set AIWAF_STORAGE_MODE = "csv" and AIWAF_DATA_DIR in project settings. |
| Legacy FastAPI | Pass storage={"backend": "csv", "data_dir": "/absolute/path/aiwaf_data"} to AIWAF. Its default backend remains memory. |
| Native adapters | Pass the lowercase data_dir in the dedicated native profile. Native adapters do not inherit legacy framework settings. |
Startup imports legacy runtime CSV data once, preserving block expiry and metadata. Original sources remain intact; backups and completion markers support recovery and prevent removed entries from returning on restart. Existing canonical policy wins conflicts. Stop old workers before upgrading and use the same absolute directory for the application and CLI. See migration paths and recovery.
Flask management searches existing project aiwaf_data and .aiwaf/data before user/package directories. Explicit AIWAF_DATA_DIR or --data-dir takes priority. The five policy files use UTF-8 across Python and Rust. Sharing these files does not make memory rate buckets, model history, or optional memory form timing distributed.
Flask and FastAPI native honeypot cache
Python 1.1.8 with aiwaf-rust 0.2.7 adds opt-in Redis and supplied-cache timing to both native adapters. CSV remains their default. Rust retains timing rules and exemptions; only timestamp I/O uses the cache.
import os
profile = {
"data_dir": "aiwaf_data",
"honeypot_backend": "redis",
"honeypot_redis_url": os.environ["AIWAF_REDIS_URL"],
"honeypot_cache_namespace": "myapp:honeypot:v1",
}
# Flask:
from aiwaf.flask.csv_pipeline import NativeAIWAF
NativeAIWAF(app, config=profile)
# FastAPI alternative:
# from aiwaf.fast.csv_pipeline import NativeAIWAFMiddleware
# app.add_middleware(NativeAIWAFMiddleware, config=profile)
For Flask-Caching or a compatible custom object, use honeypot_backend="cache" and pass honeypot_cache=cache to the native adapter. It must provide synchronous get(key) and set(key, value, timeout=seconds), preserve the timestamp/expiry pair, honor TTL and propagate errors. A false write result means failure; silent/no-op backends are unsuitable. Local caches remain worker-local. Use a shared backend and the same namespace across workers; isolate unrelated applications with different namespaces.
Records have a 300-second TTL. Missing/evicted timestamps allow the timing check, while cache errors and invalid records return generic HTTP 503. Expired-page POSTs return 409 until a new GET or TTL expiry; old snapshots are not deleted. FastAPI runs cache-backed native checks in its worker thread pool so synchronous cache I/O does not block the event loop. Other policy stores and legacy middleware behavior are unchanged.
Django native honeypot cache
Python 1.1.8 with aiwaf-rust 0.2.7 defaults Django native honeypot timing to Django's configured cache. Set honeypot_cache_alias in AIWAF_CSV_PIPELINE to select an alias; it defaults to default. Rust calls a small Python cache adapter for timestamp I/O and retains timing decisions, logical expiry and exemptions. Other policy CSVs and rate-limit configuration are unchanged.
AIWAF_CSV_PIPELINE = {
"data_dir": "aiwaf_data",
"honeypot_cache_alias": "default",
}
# Configure CACHES["default"] with a shared backend for multiple workers.
Redis or Memcached can share timestamps across workers. LocMemCache is supported with a startup warning; it remains worker-local. DummyCache is rejected when honeypot protection is enabled. Cache errors, failed writes and invalid records produce generic HTTP 503; known ignored-cache-error modes are rejected.
Entries use a 300-second TTL. Expired-page POSTs return 409 until a new GET or TTL expiry. No eager deletion occurs, so an old POST cannot erase a concurrent refresh. A missing or evicted timestamp allows the POST timing check, as before; cache failure is not treated as a miss. Backends do not transfer existing timestamps. Install the matching Rust extension and restart workers together.
Explicit honeypot_backend="csv" retains filesystem timing; "memory" uses Rust's per-engine map. Flask, FastAPI and direct native construction keep CSV as the default. See the Django cache documentation.
Training replay protection
Python 1.1.6 with aiwaf-rust 0.2.5 uses Rust-owned receipts in Flask, FastAPI, and Django. Reusing requests already handled by training cannot extend or recreate a block. Fresh requests must independently satisfy enforcement rules; models and keyword learning can still reuse historical logs.
Keep training_evidence.json across expiry, manual unblocking, log rotation, and restarts. It lives in the policy data directory unless AIWAF_TRAINING_STATE_DIR selects another durable directory. Trainers sharing a blocklist must share that directory. Receipts start with this release and cannot reconstruct decisions made by older versions. Missing native support and corrupt state stop training visibly. Block and receipt writes are separate, so a crash between them can require recovery.
Flask CLI listing now filters expired entries without rewriting an older snapshot over blocks concurrently added or refreshed by a trainer. The CLI displays the canonical added_date, with the older timestamp as a fallback. This listing fix and training replay protection address different causes of block changes. A first temporary block defaults to 900 seconds; later reputation decisions can increase its duration. Check the stored expiry rather than assuming every block lasts 24 hours.
