Project
←Back to projects
february 25, 2026
Mamoru-WAF
A zero-dependency Go Web Application Firewall with a Bubble Tea TUI and atomic hot-reloading rule engine — sub-2ms overhead, under 30MB resident memory, OWASP Top 10 protection as a single binary.
Mamoru-WAF
A high-performance reverse proxy and Web Application Firewall built in Go with zero external runtime dependencies. Mamoru sits in front of any HTTP backend, evaluates every incoming request against a hot-reloadable rule engine, and blocks SQL injection, XSS, path traversal, and automated scanner traffic before it reaches the application — all from a single binary under 15MB.
Overview
Enterprise WAFs assume infrastructure that most teams don't have: Redis for rate limiting state, PostgreSQL for logging, a dedicated DevOps team to manage rolling rule updates. For a solo developer, a microservice, or a containerized sidecar deployment, this overhead introduces unnecessary cost and operational burden without proportional security gain.
Mamoru-WAF collapses that complexity. Drop the binary in front of any HTTP service, point it at a YAML config file, and get OWASP Top 10 protection in under 60 seconds. No database. No external state. No infrastructure dependencies.
Architecture
Mamoru's architecture separates concerns cleanly across three layers: the proxy layer, the rule engine, and the operator interface.
Reverse Proxy Core
The proxy layer is a standard Go httputil.ReverseProxy wrapped with middleware that intercepts every inbound request before forwarding. The target service is opaque to the outside world — clients connect to Mamoru's listen port and never reach the backend directly. Timeout configuration, connection pooling, and error propagation are all managed at this layer.
AtomicEngine Rule Evaluation
The rule engine is where Mamoru's core security logic lives. Rules are evaluated sequentially: first match wins, which makes rule ordering deterministic and auditable. Each rule specifies a target (url, body, headers, or all), a regex pattern, an action (block or allow), and a response status code.
The engine is managed by an AtomicEngine struct that holds the active ruleset behind an atomic pointer. When rules.yaml changes on disk, a file watcher detects the modification and compiles the new ruleset in a background goroutine. Once compilation succeeds, the pointer is swapped atomically — in-flight requests continue evaluating against the old ruleset until they complete, while all new requests pick up the updated rules immediately. No connections are dropped. No restart is required. Rule changes propagate within 2 seconds.
The fail_safe configuration key controls behavior if the engine itself crashes: open lets traffic through, block rejects everything. This is an explicit operational trade-off the operator declares, not a silent default.
Bubble Tea TUI
When tui.enabled: true, Mamoru launches an interactive terminal dashboard built with the Bubble Tea framework. The TUI renders four live panels: a real-time event log of blocked requests (with rule name, source IP, and timestamp), backend health indicators, requests-per-second and block rate statistics, and an active rule summary with per-rule match counts.
The TUI runs in the same process as the proxy on a separate goroutine, connected via channels. Keyboard shortcut d toggles between block and detect modes live without touching the config file. r forces an immediate rule reload. For production or CI deployments, --headless disables the TUI entirely and writes structured logs to stdout or a file.
Key Features
- OWASP Top 10 patterns built-in — regex rules for SQLi (
union,select,drop,sleep), XSS (<script,javascript:,onerror=), path traversal (../,%2e%2e%2f), and automated scanner User-Agents (sqlmap,nikto,masscan) - Per-path rate limiting — global rate limits with endpoint-specific overrides; the
/api/loginexample ships at 5 req/s with a burst of 10 - Atomic hot-reload — rule changes applied within 2 seconds, zero dropped connections, compile-then-swap semantics
- Headless mode —
--headless --log-file /var/log/mamoru.logfor production deployments; structured log output compatible with log aggregators - Docker Compose sidecar pattern — purpose-built for the
depends_on+MAMORU_TARGETenvironment variable deployment model - Extensible rule interface — custom rules implement a two-method Go interface (
Name() string,Evaluate(*http.Request) (bool, string)) and register with the engine initializer
Implementation Details
Zero External Dependencies
The dependency constraint was a deliberate design choice, not an arbitrary limitation. External dependencies in a security tool introduce supply chain risk — a compromised package in a WAF's dependency tree is significantly more dangerous than one in an application. By restricting Mamoru to the Go standard library plus a single TUI framework, the entire codebase is auditable. The net/http/httputil reverse proxy, regexp for pattern matching, sync/atomic for engine swaps, and os for file watching cover everything needed.
Regex Compilation Strategy
Patterns are compiled once during ruleset load, not on each request evaluation. A ruleset reload compiles all patterns upfront in the background goroutine — if any pattern is invalid, the entire reload fails cleanly and the existing ruleset remains active. This prevents a misconfigured rules.yaml from taking down protection.
Rate Limiter Design
The rate limiter uses Go's golang.org/x/time/rate token bucket implementation scoped per source IP. Per-path overrides are matched before the global limit — a request to /api/login is checked against the login-specific limiter first. State is in-process memory, which means rate limit counters reset on restart. This is an intentional trade-off: for a single-instance deployment, in-memory state is simpler and faster than an external store.
Tech Stack Rationale
| Component | Technology | Rationale |
|---|---|---|
| Runtime | Go | Compiled binary, low memory footprint, native concurrency primitives, no GC pauses under normal WAF-scale load |
| TUI | Bubble Tea | Elm-architecture model makes the dashboard state machine predictable; composable components for multi-panel layouts |
| Rule config | YAML | Human-readable, diff-friendly, trivial to version-control alongside application code |
| Packaging | Docker | Single-file Compose sidecar pattern; depends_on + environment variable config is idiomatic for microservice deployment |
At steady state: under 2ms added latency per request, under 30MB resident memory, binary size under 15MB. Handles thousands of requests per second on a single CPU core — appropriate for protecting services from the hobby tier up to moderate production traffic.