Command Menu

Search pages, blogs, projects, and more...

Mamoru-WAF

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.

GoBubble TeaYAMLDocker

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/login example 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.log for production deployments; structured log output compatible with log aggregators
  • Docker Compose sidecar pattern — purpose-built for the depends_on + MAMORU_TARGET environment 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

ComponentTechnologyRationale
RuntimeGoCompiled binary, low memory footprint, native concurrency primitives, no GC pauses under normal WAF-scale load
TUIBubble TeaElm-architecture model makes the dashboard state machine predictable; composable components for multi-panel layouts
Rule configYAMLHuman-readable, diff-friendly, trivial to version-control alongside application code
PackagingDockerSingle-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.