ReviewOS

zig-utils/zig-waf

[WAF-39] Enforce request-path throughput, p99, and RSS release gates

#40
Closed chris opened this 9 days ago ·
9 days ago

Objective

Gate releases on at least 1.25x throughput, at most 80 percent p99 added latency, and no higher peak RSS than the faster pinned baseline under defined CRS workloads.

Deliverables

  • Implement the complete scoped behavior described above.
  • Keep request-path work independent from UI, PostgreSQL, and blocking audit I/O.
  • Use Zig 0.17-dev and dependencies pinned through Pantry; do not add Git submodules.
  • Keep public APIs explicit about ownership, resource limits, errors, security policy, and compatibility.
  • Add focused unit, integration, differential, fuzz, benchmark, and documentation coverage as applicable.

Acceptance criteria

  • The scoped implementation and tests are merged on main in small conventional commits.
  • Zig formatting, focused tests, and the relevant CI suite pass.
  • Compatibility claims have executable evidence and no unexplained skips or silent fallbacks.
  • Untrusted inputs have deterministic bounds; hot paths have benchmark evidence.
  • Documentation and the machine-readable compatibility matrix are updated.

Relationships

Blocked by

Blocks

Baselines

  • ModSecurity 3.0.16
  • Coraza 3.7.0
  • OWASP CRS 4.28.0
  • libinjection 4.0.0
  • Zig 0.17-dev resolved and locked through Pantry
9 days ago

Complete. zig build gate-request-path, enforced in CI on every push.

What is measured — the per-request work against a compiled rule set exercising @rx, @detectSQLi, @pm, @contains, and @ipMatch, over a mix of benign and attacking requests. Clean traffic alone would not touch the paths that matter: matching, capturing, and building an intervention.

Throughput, p99, and RSS — reported together with p50 and p999, because a WAF's cost is paid on every request and the number that matters is the tail, not the average.

developer machineCI runnerthreshold
p507.8 µs16.1 µs—
p999.6 µs21.6 µs200 µs
p99911.8 µs31.8 µs—
throughput126,719 rps62,701 rps≥ 5,000 rps
peak RSS3.9 MB3.8 MB256 MB

Why the thresholds are loose — a shared runner is about twice as slow as a laptop here, and that varies run to run. A tight bound fails for reasons unrelated to the change under test, and a gate that cries wolf gets turned off. These are set to catch a regression in kind: a rule set that started recompiling per request, an allocation that became a leak, a matcher that lost its cache. The measured numbers are printed on every run, so a gradual drift is visible in the log even while the gate stays green.

The gate checks itself — a run in which no request was blocked fails, whatever the timings say. A rule set that silently stopped evaluating would otherwise sail through every threshold it has, which is the way a performance gate most easily becomes meaningless.

Complements the existing bench-* steps (parser, plan, directives, transformations, ownership, lifecycle, ingestion), which measure components rather than the request path as a whole.

Sign in to comment on this issue.