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

RuntimeInputResolution
FlaskFlat app.config['AIWAF_*'] settings; constructor middleware selectionLoad application configuration before AIWAF(app). A shell variable does not automatically become Flask app.config. The middleware reads adapter settings and its own defaults.
DjangoFlat settings in settings.pyExplicit 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.
FastAPINested constructor options or AIWAFConfigDefaults, 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.jsOptions to the framework adapterThe 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/SpringAiwafConfig, nested JSON, environment, Spring propertiesSpring's loader creates defaults/file configuration, applies supported environment mappings, then Spring property lookups. Custom configuration beans replace auto-configuration where supported.
Rust/WASMArguments to primitive bindingsThese 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

SettingTypeSource default
storage.backendstr"memory"
storage.file_pathstr"aiwaf_data.json"
header_validation.enabledbooltrue
header_validation.block_suspiciousbooltrue
header_validation.quality_thresholdint3
header_validation.exempt_pathslist["/health", "/healthz", "/status", "/ping", "/metrics", "/favicon.ico", "/robots.txt"]
header_validation.custom_suspicious_patternslist[]
header_validation.custom_legitimate_patternslist[]
header_validation.trust_legitimate_botsboolfalse
rate_limiting.enabledbooltrue
rate_limiting.max_requestsint20
rate_limiting.window_secondsint10
rate_limiting.flood_thresholdint40
rate_limiting.key_modestr"ip_path"
rate_limiting.soft_block_blocklistboolfalse
rate_limiting.cache_backendstr"memory"
rate_limiting.redis_urlnull or stringnull
rate_limiting.cache_key_prefixstr"aiwaf:rate:"
rate_limiting.exempt_ipslist[]
ip_keyword_block.enabledbooltrue
ip_keyword_block.sql_injection_modestr"block"
ip_keyword_block.payload_max_bytesint65536
ip_keyword_block.malicious_keywordslist[".php", "xmlrpc", "wp-", ".env", ".git", ".bak", "shell", "filemanager"]
honeypot.enabledbooltrue
honeypot.min_form_timefloat1.0
honeypot.max_page_timeint240
geo_block.enabledboolfalse
geo_block.allow_countrieslist[]
geo_block.block_countrieslist[]
ai_anomaly.enabledbooltrue
uuid_tamper.enabledbooltrue
logging_middleware.enabledbooltrue
logging_middleware.log_dirstr"aiwaf_logs"
logging_middleware.log_formatstr"combined"
blocklist.default_block_durationint3600
blocklist.permanent_block_thresholdint5
blocklist.auto_unblock_enabledbooltrue
blocklist.cleanup_intervalint3600
exemptions.private_ips_exemptedbooltrue
exemptions.localhost_exemptedbooltrue
exemptions.auto_exempt_patternslist["127.0.0.1", "::1", "192.168.*.*", "10.*.*.*", "172.16.*.*"]
security.log_blocked_requestsbooltrue
security.log_suspicious_requestsbooltrue
security.max_header_lengthint8192
security.max_user_agent_lengthint512
performance.enable_cachingbooltrue
performance.cache_durationint300
performance.max_cache_entriesint10000
logging.levelstr"INFO"
logging.formatstr"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logging.log_filenull or stringnull
path_ruleslist[]

Node.js normalized option defaults

SettingTypeSource default
staticKeywordsarray[]
dynamicTopNnumber10
WINDOW_SECnumber60
MAX_REQnumber100
FLOOD_REQnumber200
HONEYPOT_FIELDoptionalunset (resolved by consumer)
AIWAF_MIN_FORM_TIMEnumber0
AIWAF_MAX_PAGE_TIMEnumber3600
AIWAF_LOGIN_PATH_PREFIXESarray[]
AIWAF_POST_ONLY_SUFFIXESarray[]
AIWAF_ALLOWED_METHODSarray[]
AIWAF_METHOD_POLICY_ENABLEDbooleanfalse
AIWAF_EXEMPT_PATHSarray[]
AIWAF_EXEMPT_IPSarray[]
AIWAF_ALLOWED_PATH_KEYWORDSarray[]
AIWAF_EXEMPT_KEYWORDSarray[]
AIWAF_EXEMPTIONS_DBbooleantrue
AIWAF_HEADER_VALIDATIONbooleanfalse
AIWAF_REQUIRED_HEADERSarray[]
AIWAF_HEADER_QUALITY_MIN_SCOREnumber3
AIWAF_MAX_HEADER_BYTESnumber32768
AIWAF_MAX_HEADER_COUNTnumber100
AIWAF_MAX_USER_AGENT_LENGTHnumber500
AIWAF_MAX_ACCEPT_LENGTHnumber4096
AIWAF_BLOCKED_USER_AGENTSarray["sqlmap","nikto","acunetix","nmap","masscan"]
AIWAF_GEO_BLOCK_ENABLEDbooleanfalse
AIWAF_GEO_BLOCK_COUNTRIESarray[]
AIWAF_GEO_ALLOW_COUNTRIESarray[]
AIWAF_GEO_MMDB_PATHoptionalunset (resolved by consumer)
AIWAF_GEO_CACHE_SECONDSnumber3600
AIWAF_GEO_CACHE_PREFIXstring"aiwaf:geo:"
AIWAF_DYNAMIC_KEYWORDS_DBbooleantrue
AIWAF_MODEL_STORAGEstring"file"
AIWAF_MODEL_PATHoptionalunset (resolved by consumer)
AIWAF_MODEL_STORAGE_FALLBACKstring"file"
AIWAF_MODEL_CACHE_KEYoptionalunset (resolved by consumer)
AIWAF_MODEL_CACHE_TTLnumber0
AIWAF_MIDDLEWARE_LOGGINGbooleanfalse
AIWAF_MIDDLEWARE_LOG_PATHstring"logs/aiwaf-requests.jsonl"
AIWAF_MIDDLEWARE_LOG_DBbooleanfalse
AIWAF_MIDDLEWARE_LOG_CSVbooleanfalse
AIWAF_MIDDLEWARE_LOG_CSV_PATHstring"logs/aiwaf-requests.csv"
AIWAF_CAPTURE_EXTENDED_REQUEST_INFObooleanfalse
AIWAF_EXTENDED_INFO_MAX_HEADERSnumber50
AIWAF_EXTENDED_INFO_MAX_VALUE_LENGTHnumber512
AIWAF_MIN_AI_LOGSnumber0
AIWAF_ENABLE_KEYWORD_LEARNINGbooleantrue
AIWAF_MIDDLEWARESoptionalunset (resolved by consumer)
AIWAF_DISABLE_MIDDLEWARESarray[]
AIWAF_PATH_RULESarray[]
AIWAF_PATH_MANIFESTstring".aiwaf/paths.json"
AIWAF_ROUTE_PLAN_VERSIONnumber0
AIWAF_ACCESS_LOGoptionalunset (resolved by consumer)
AIWAF_RATE_CACHE_FAILURE_MODEstring"closed"
AIWAF_FORCE_JSON_ERRORSbooleantrue

Java AiwafConfig field defaults

Java expressions are shown as declared in source. Collection constructors describe initial values; constants are resolved by the Java implementation.

SettingTypeSource default
headerValidationEnabledbooleantrue
rateLimitEnabledbooleantrue
rateLimitMaxint20
rateLimitWindowSecondsint10
rateLimitScopeRateLimitScopeRateLimitScope.PER_PATH
blockIpOnRateLimitBreachbooleanfalse
blockIpOnFloodBreachbooleanfalse
rateLimitFloodThresholdint40
trustedProxyCidrsSet<String>new HashSet<>(Set.of("127.0.0.0/8", "::1/128"))
maxForwardedForEntriesint16
exemptIpsSet<String>new HashSet<>()
exemptIpPatternsSet<String>new HashSet<>()
privateIpsExemptedbooleantrue
localhostExemptedbooleanfalse
autoExemptPathPrefixesSet<String>new HashSet<>()
exemptPathsSet<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" ))
exemptAllowWildcardsbooleantrue
exemptAllowPrefixbooleantrue
geoBlockEnabledbooleanfalse
geoAllowedCountriesSet<String>new HashSet<>()
geoBlockedCountriesSet<String>new HashSet<>()
geoExemptPathsSet<String>new HashSet<>()
geoIpDatabasePathStringnull
geoCacheSecondsint3600
geoMaxCacheEntriesint10_000
honeypotEnabledbooleantrue
minFormTimeSecondsdouble1.0
maxFormPageTimeSecondsdouble240.0
loginMinFormTimeSecondsdouble0.1
loginPathPrefixesSet<String>new HashSet<>(Set.of( "/admin/login/", "/login/", "/accounts/login/", "/auth/login/", "/signin/" ))
uuidTamperEnabledbooleantrue
uuidScoreEnabledbooleantrue
uuidScoreWindowSecondsint60
uuidScoreBlockThresholdint5
uuidMalformedWeightint5
uuidNotFoundWeightint1
uuidSuccessDecayint2
uuidParameterNamesSet<String>new HashSet<>(Set.of("uuid"))
ipKeywordBlockEnabledbooleantrue
methodValidationEnabledbooleantrue
allowedMethodsSet<String>new HashSet<>(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"))
enabledMiddlewaresSet<String>new HashSet<>()
disabledMiddlewaresSet<String>new HashSet<>()
maxHeaderBytesint32 * 1024
maxHeaderCountint100
maxUserAgentLengthint500
maxAcceptLengthint4096
maxRequestBodyBytesint1024 * 1024
requestBodyInspectionBytesint64 * 1024
requestBodyInspectionEnabledbooleantrue
sqlInjectionModeString"block"
allowCompressedRequestBodiesbooleanfalse
maxRuntimeStateEntriesint10_000
maxParameterCountint200
maxParameterBytesint16 * 1024
allowDuplicateParametersbooleanfalse
allowedContentTypesByPathPrefixMap<String, Set<String>>new HashMap<>()
minHeaderQualityScoreint3
requiredHeadersList<String>new ArrayList<>(HeaderValidationCore.REQUIRED_HEADERS)
requiredHeadersByMethodMap<String, List<String>>new HashMap<>()
blockedPathPatternsSet<String>new HashSet<>(Arrays.asList(".env", "../", "%2e%2e", "wp-admin", "phpmyadmin", ".git"))
pathRulesList<PathRule>new ArrayList<>()
pathManifestEnabledbooleantrue
pathManifestPathString".aiwaf/paths.json"
aiEnabledbooleantrue
aiLazyLoadModelbooleantrue
aiBackgroundPreloadbooleanfalse
aiModelPathString"aiwaf-model.bin"
aiAnomalyScoreThresholddouble0.7
aiRequireBehaviorConfirmationbooleantrue
aiRecentWindowSecondsint300
aiMinRecentSamplesToBlockint5
aiTimingLogsEnabledbooleanfalse
enableKeywordLearningbooleantrue
dynamicTopNint10
exemptKeywordsSet<String>new HashSet<>()
legitimatePathKeywordsSet<String>new HashSet<>()
storeExtendedBlockInfobooleantrue
blockInfoMaxHeadersint50
blockInfoMaxHeaderValueLengthint512
blockInfoRedactHeadersSet<String>new HashSet<>(Set.of("authorization", "cookie", "set-cookie"))
sensitiveParameterNamesSet<String>new HashSet<>(Set.of( "password", "passwd", "pwd", "token", "access_token", "refresh_token", "secret", "api_key", "apikey", "authorization", "code", "session", "sessionid" ))
logQueryParametersbooleanfalse
observabilityEnabledbooleantrue
loggingEnabledbooleantrue
logDirString"aiwaf_logs"
logFormatString"combined"
storageBackendString"memory"
storageFilePathStringnull
storageRedisUrlStringnull
storageKeyPrefixString"aiwaf:"
storageRedisModeString"standalone"
storageRedisFailureModeString"fail_closed"
storageRedisConnectionTimeoutMillisint1_000
storageRedisSocketTimeoutMillisint2_000
storageRedisPoolMaxTotalint32
storageRedisPoolMaxIdleint16
storageRedisPoolMinIdleint0
storageRedisPoolMaxWaitMillisint250
legitimateRouteHintsSet<String>new HashSet<>()

Source loaders and complete adapter references

Legacy adapter native CSV settings

These uppercase settings apply to the retained legacy adapters. The explicit native pipeline uses a separate lowercase profile.

SettingDefault and scope
AIWAF_USE_RUST, AIWAF_RUST_CSVEnabled 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 variableDisables shared native dispatch outside Flask/Django application contexts. Configured framework settings take precedence over this environment fallback.
AIWAF_CSV_REQUEST_CACHEFlask 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_FSYNCFlask CSV logging defaults to False. True synchronizes each row to disk. Changed policy-state writes still synchronize.
AIWAF_RUST_ISO_DATES=1Optional 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_PATHOptional 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 keyDefault and meaning
enabledAll eight stage names; explicitly select a subset if needed
data_diraiwaf_data; native policy CSV directory
exempt_paths / exempt_ips/health, /robots.txt, /sitemap.xml; no IPs. Literal case-insensitive path prefixes
sql_mode / max_bytesblock / 65536 bytes; off supported, monitor rejected; bounded structured traversal
learning / top_ntrue / 10; sorted stable presence-keyword subset
geo_allow / geo_blockEmpty arrays plus stored blocked countries; unknown country allowed
rate_backend / rate_key_modememory / ip_path; csv shares buckets, ip aggregates paths; Redis rejected
rate_window / rate_max / rate_flood10 seconds / 20 / 40; throttle 429, flood 403 with blocklist persistence
honeypot_backendDjango 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_authenticated1.0 seconds / 240 seconds / true
uuid_score_enabled / uuid_window / uuid_thresholdtrue / 60 seconds / 5; malformed weight 5, not-found weight 1, success decay 2
required_headers / header_min_scoreHTTP_USER_AGENT and HTTP_ACCEPT / 3; empty required list retains safety caps
anomaly_window60 seconds; history and loaded model inference are native and per worker
log_path / log_fsyncdata_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 keysDefaults and meaning
enabledAll eight stages
exempt_paths / exempt_ips/health, /robots.txt, /sitemap.xml; empty IPs; literal case-insensitive prefixes
sql_mode / max_bytesblock / 65536; off supported, monitor rejected
rate_window / rate_max / rate_flood / rate_key_mode10 seconds / 20 / 40 / ip_path (ip also supported); local Rust counters
min_form_time / max_page_time / skip_authenticated / honeypot_field1.0 seconds / 240 seconds / true / empty field; memory timing per engine
geo_allow / geo_block / blocklistEmpty; blocklist IP maps to Unix expiry seconds, 0 permanent. Unknown country allowed
static_keywords / dynamic_keywords / legitimate_keywords / learningNative vocabulary / empty / exact legitimate route names / true; bounded probe learning
allowed_methods / uuid_prefix / uuid_window / uuid_thresholdNo restriction / /user/ / 60 seconds / 5; malformed UUID rejected
required_headers / header_min_scoreHTTP_USER_AGENT and HTTP_ACCEPT / 3; safety caps retained
anomaly_window / max_history / max_clients / max_pending60 seconds / 1000 / 100000 / 10000; bounded local state
block_duration86400 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.

IntegrationCSV directory configuration
Legacy FlaskSet app.config['AIWAF_USE_CSV'] = True and app.config['AIWAF_DATA_DIR'] before initializing AIWAF.
Legacy DjangoSet AIWAF_STORAGE_MODE = "csv" and AIWAF_DATA_DIR in project settings.
Legacy FastAPIPass storage={"backend": "csv", "data_dir": "/absolute/path/aiwaf_data"} to AIWAF. Its default backend remains memory.
Native adaptersPass 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.