Shared network helpers. Currently the trusted-proxy client-IP resolver,
reused by rate limiting, structured logging and future Geo/IP-reputation —
the real client IP is derived once and read by everyone from
RequestContext::client_ip.
Shared state seam (BOUNDARY §1.5/§4). The StateStore trait is the public ABI
onto which the enterprise multi-node store (Redis/shared) plugs in without a
fork; the in-memory token-bucket implementation is the OPEN single-node default.
Lives in waf-core (not a separate waf-state crate) because the consumers
(waf-detection’s rate limiter, waf-proxy’s wiring) already depend on
waf-core — placing it here introduces no dependency cycle.
CRS/ModSecurity rule import (B2). Loads SecRule … files at boot and runs the
supported subset as a WafModule. The parser is the OPEN baseline (BOUNDARY.md
§1.7); rules a file uses that fall outside the v1 subset are skipped and reported at
boot, never silently dropped.
GraphQL module configuration (Phase 11). Structural DoS/abuse caps applied to the
GraphQL operation(s) carried by a request (JSON query field, application/graphql
raw body, or GET ?query=). All counts come from the lexical [graphql_lex] pass.
gRPC module configuration (gRPC phase). Structural DoS/abuse caps on the framed
protobuf body (message size / field count / nesting depth) + a compressed-payload
policy. Structural only — protobuf field CONTENT flows to the normal content modules
via the §6 derived channel, not through this module. Counts come from the
grpc_extract pass.
Prometheus /metrics exposition. OPEN baseline (BOUNDARY.md §1.6). Served on a
SEPARATE loopback listener — never the data port, which would let the WAF inspect/expose
its own internal posture (blocked/rate-limited volumes are an info leak if reachable).
Canonicalized version of all inspectable fields.
Populated by the normalizer before any detection module runs.
Raw originals remain in RequestContext fields.
One severity-tagged contribution emitted inside Decision::Scores.
The module reports what it found (rule_id + severity); the pipeline
owns the severity -> points policy.
Inbound TLS termination configuration. Basic termination, cert from file is the
OPEN core surface (BOUNDARY.md §3.2): single-node self-sufficiency. ACME/rotation/
multi-node certs / mTLS-with-managed-PKI are ENTERPRISE and plug in behind the
TlsCertSource seam (see waf-proxy::tls).
Proxy-Wasm plugin runtime (B3). Loads one or more .wasm filters at boot and runs each
as a WafModule on every request (structural). The runtime is the OPEN baseline
(BOUNDARY.md §1.7); a plugin whose .wasm cannot be compiled/instantiated is logged
and skipped (fail-open at load, like CRS) while the per-request posture is fail-closed.
What to do with a gRPC message whose payload is COMPRESSED (per-message flag set or a
non-identity grpc-encoding): its bytes are opaque to the WAF, so it cannot be
inspected. Reject (default) is fail-closed — a gzip payload you let through is a
trivial bypass (compress the attack, skip the WAF). Passthrough forwards it
UNINSPECTED — a deliberate, on-record choice for a backend that requires compression.
What to do when a given failure scenario occurs. Uniform across scenarios for
schema consistency, but the meaning of FailOpen is scenario-specific — see
ResilienceConfig and ARCHITECTURE §9.
Highest paranoia level the contract allows. The validator guards the legal
space of the CONTRACT (see ARCHITECTURE §7), not the current rule set — PL4 is
forward-compatible even if no rule uses it yet.
Upper bound on trusted_hops: real proxy chains are short; a huge value is a
configuration mistake (and would make every request fall back to the peer).