rwarden is a developer CLI for testing path security rules, verifying RouteWarden configuration files offline, and emitting the official JSON Schema.
Download and install the latest pre-compiled release binary automatically:
curl -fsSL https://routewarden.github.io/cli/install.sh | bashCustom installation directory (e.g. ~/.local/bin):
curl -fsSL https://routewarden.github.io/cli/install.sh | INSTALL_DIR=$HOME/.local/bin bash(Or via raw GitHub fallback: curl -fsSL https://raw.githubusercontent.com/routewarden/cli/main/install.sh | bash)
Download standalone binaries for Linux (amd64, arm64), macOS (Apple Silicon arm64 & Intel amd64), and Windows directly from GitHub Releases.
Run rwarden via container without installing any local binaries:
# Validate configuration file directly
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest validate --config /routewarden.json
# Test path interactively
docker run --rm ghcr.io/routewarden/cli:latest test --path "/.env"git clone https://github.com/routewarden/cli.git
cd cli
go build -o /usr/local/bin/rwarden .Verify installation:
rwarden version
# rwarden version 3.0.0To update rwarden to the newest released version:
Re-running the universal installer automatically queries GitHub Releases for the latest version tag, downloads the matching pre-compiled binary, and safely replaces your existing binary:
# Default system-wide (/usr/local/bin):
curl -fsSL https://routewarden.github.io/cli/install.sh | bash
# Or custom user directory (~/.local/bin):
curl -fsSL https://routewarden.github.io/cli/install.sh | INSTALL_DIR=$HOME/.local/bin bashIf using the containerized client, pull the latest image tag:
docker pull ghcr.io/routewarden/cli:latestPull the latest commits and rebuild:
cd cli
git pull origin main
go build -o /usr/local/bin/rwarden .Confirm the upgraded version:
rwarden versionThe CLI repository includes scripts/update-version.sh to synchronize version tags across version.json, Go source files (main.go), installer fallbacks, documentation manifests, and package.json:
# Update version across all files and version.json
./scripts/update-version.sh v1.1.0
# Or using npm
npm run version:update v1.1.0For full details on the release workflow, see VERSIONING.md.
rwarden installs as a single standalone binary without hidden system daemons or dependencies. To remove it:
# Default system-wide installation:
sudo rm -f /usr/local/bin/rwarden
# Or user-local installation:
rm -f ~/.local/bin/rwardenSimulate candidate path extraction, normalization, and pattern matching on an arbitrary path without starting Traefik, Caddy, or NGINX:
CLI:
# Test a sensitive file path (positional or --path)
rwarden test /.env
rwarden test --path "/.env"
# Test double URL encoding anti-evasion
rwarden test "/static/%252e%252e/.env"
# Test query string inspection (-q or --query)
rwarden test -q "file=secret.conf" /search
# Test custom HTTP methods (-X, -m, or --method)
rwarden test -X POST /wp-config.php
# Test custom HTTP headers (-H or --header, repeatable)
rwarden test -H "X-Forwarded-Uri: /.env" /api
# Test against a custom RouteWarden config file (-c or --config)
rwarden test -c routewarden.json /admin/dashboard
# Test client IP whitelisting
rwarden test -c routewarden.json --ip "10.0.0.1" /admin
# Pipe configuration via stdin
cat routewarden.json | rwarden test -c - /adminDocker:
# Test a sensitive path
docker run --rm ghcr.io/routewarden/cli:latest test /.env
# Test evasion via query inspection
docker run --rm ghcr.io/routewarden/cli:latest test -q "file=secret.conf" /search
# Test against custom config file
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest test -c /routewarden.json --ip "10.0.0.1" /admin| Flag | Type | Default | Description |
|---|---|---|---|
[path], --path |
string | "" |
Request path to evaluate (e.g. /.env or /api/v1) |
-c, --config |
string | "" |
Optional path to routewarden.json (or - for stdin) |
-q, --query |
string | "" |
Optional request query string to evaluate |
-X, -m, --method |
string | "GET" |
HTTP method (e.g. GET, POST, HEAD) |
--ip |
string | "" |
Optional client IP address to evaluate against allowedIps |
-H, --header |
string | "" |
Optional header in Key:Value format to test (repeatable) |
--check-query |
bool | true |
Enable or disable query string inspection |
Example Output:
🔍 Testing: GET /static/%252e%252e/.env
Candidate paths extracted (2):
- /static/%252e%252e/.env
- /.env
Result: 🛑 BLOCKED (HTTP Status 403)
Reason: block_pattern_match
Target: /.env
Pattern: (?i)\.env
Validate a RouteWarden JSON configuration file before deploying:
CLI:
# Validate routewarden.json directly (positional or --config)
rwarden validate routewarden.json
rwarden validate -c routewarden.json
# Auto-detects routewarden.json in current directory if omitted
rwarden validate
# Validate piped config via stdin
cat routewarden.json | rwarden validateDocker:
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest validate /routewarden.jsonExample Output:
✓ Configuration routewarden.json is VALID.
- Enabled: true
- Default patterns enabled: true
- Default allow patterns enabled: true
- Methods: [GET]
- Custom block patterns: 2
- Monitored headers: [X-Forwarded-Uri]
Exits with non-zero status code if invalid regexes, CIDRs, or configuration options are encountered, making it ideal for CI/CD pipelines.
Print the official RouteWarden configuration JSON Schema:
CLI:
rwarden schema > routewarden.schema.jsonDocker:
docker run --rm ghcr.io/routewarden/cli:latest schema > routewarden.schema.jsonConvert routewarden.json into native gateway configuration — no manual translation required.
CLI:
# Traefik: dynamic YAML middleware definition
rwarden generate traefik-yaml [routewarden.json] > dynamic.yml
rwarden generate yaml > dynamic.yml
# Traefik: dynamic TOML middleware definition
rwarden generate traefik-toml [routewarden.json] > dynamic.toml
rwarden generate toml > dynamic.toml
# Traefik: Docker Compose labels block
rwarden generate traefik-labels [routewarden.json]
rwarden generate compose
# Caddy: Caddyfile directive block
rwarden generate caddy [routewarden.json]
# NGINX / OpenResty: Lua init table for nginx.conf
rwarden generate nginx [routewarden.json]Docker:
# Traefik: dynamic YAML middleware definition
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest generate traefik-yaml /routewarden.json > dynamic.yml
# Traefik: dynamic TOML middleware definition
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest generate traefik-toml /routewarden.json > dynamic.toml
# Traefik: Docker Compose labels block
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest generate traefik-labels /routewarden.json
# Caddy: Caddyfile directive block
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest generate caddy /routewarden.json
# NGINX / OpenResty: Lua init table for nginx.conf
docker run --rm -v $(pwd)/routewarden.json:/routewarden.json ghcr.io/routewarden/cli:latest generate nginx /routewarden.json| Target / Alias | Output |
|---|---|
traefik-yaml, traefik, yaml |
Traefik dynamic YAML middleware definition (dynamic.yml) |
traefik-toml, toml |
Traefik dynamic TOML middleware definition (dynamic.toml) |
traefik-labels, compose, labels |
Docker Compose labels: block |
caddy, caddyfile |
Caddyfile routewarden { ... } directive block |
nginx, openresty |
OpenResty Lua table for init_by_lua_block in nginx.conf |
Test configurations against an ephemeral Docker container running Traefik, Caddy, or NGINX.
rwarden sandbox supports:
- Generic JSON (
routewarden.json): automatically synthesized into full gateway configurations. - Actual Gateway Configs: pass real
traefik.toml,traefik.yaml/dynamic.yml,docker-compose.yaml,Caddyfile, ornginx.confdirectly. Target gateway and format are auto-detected from filename and content. - Inline Docker Labels: test Traefik label snippets directly via
--labels. - Snippets & Complete Production Configs:
- Snippets: Middleware-only snippets are automatically wrapped with sandbox entrypoints and mock upstream endpoints returning
200 OKfor passing traffic. - Complete Configs: Full configurations with backend service URLs, upstreams, or proxies are automatically adapted for ephemeral sandboxes in-memory without modifying your source file:
- Traefik: External loadBalancer service URLs (
url,service) are redirected to Traefik's internal ping service (ping@internal), entrypoints guarantee:8080binding, and router TLS directives are safely neutralized for local HTTP testing. - Caddy: Upstream
reverse_proxytargets are replaced with mock200 OKresponders,order routewarden firstandauto_https offare injected, and custom site blocks bind to:8080. - NGINX: External
upstreamhostnames are neutralized withdownto prevent OpenResty startup DNS failures,proxy_passdirectives are converted to mock Lua200 OKresponders, local SSL certificate requirements are bypassed, andlisten 8080;is configured.
- Traefik: External loadBalancer service URLs (
- Live Upstream Reachability: In
--testmode, RouteWarden recognizes upstream reachability (HTTP200, or HTTP502/504from an offline production backend) as an allowlist pass-through.
- Snippets: Middleware-only snippets are automatically wrapped with sandbox entrypoints and mock upstream endpoints returning
# 1. Test actual Traefik TOML configuration (auto-detects target & mounts dynamic.toml)
rwarden sandbox --config traefik.toml
# 2. Test actual Traefik YAML / dynamic.yml
rwarden sandbox --config dynamic.yml
# 3. Test Docker Compose with Traefik labels (extracts labels & translates to dynamic YAML)
rwarden sandbox --config docker-compose.yaml
# 4. Test Traefik labels inline
rwarden sandbox --labels "traefik.http.middlewares.shield.plugin.routewarden.enabled=true"
# 5. Test actual Caddyfile (auto-detects target caddy & wraps standalone route if needed)
rwarden sandbox --config Caddyfile
# 6. Test actual nginx.conf (auto-detects OpenResty target & mounts configuration)
rwarden sandbox --config nginx.conf
# 7. Automated live probe verification with custom path and IP allowlist assertion
rwarden sandbox --config docker-compose.yaml --test --probe-path "/immich/api/admin" --probe-ip "10.0.0.1"
# 8. Dry-run: inspect generated/wrapped gateway config & exact docker run command
rwarden sandbox --config traefik.toml --dry-run --print-config
# Ready-to-test working examples are available in the samples/ folder:
rwarden sandbox --config samples/traefik/dynamic.yml --dry-run --print-config
rwarden sandbox --config samples/traefik/dynamic.toml --dry-run --print-config
rwarden sandbox --config samples/traefik/docker-compose.yaml --dry-run --print-config
rwarden sandbox --config samples/caddy/Caddyfile --dry-run --print-config
rwarden sandbox --config samples/nginx/nginx.conf --dry-run --print-config
rwarden sandbox --target traefik --config samples/json/routewarden.json --dry-run --print-configSee samples/README.md for full documentation of each sample.
When running rwarden via Docker, mount the Docker socket (/var/run/docker.sock) so rwarden can spin up sibling gateway containers on the host, and mount the current directory or config file:
# Test actual traefik.toml via Docker
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/traefik.toml:/config/traefik.toml:ro \
-p 8080:8080 \
ghcr.io/routewarden/cli:latest sandbox --config /config/traefik.toml
# Test Docker Compose Traefik labels via Docker
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/docker-compose.yaml:/config/docker-compose.yaml:ro \
-p 8080:8080 \
ghcr.io/routewarden/cli:latest sandbox --config /config/docker-compose.yaml --test
# Test inline labels without mounting files
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 8080:8080 \
ghcr.io/routewarden/cli:latest sandbox \
--labels "traefik.http.middlewares.shield.plugin.routewarden.enabled=true" \
--test| Flag | Type | Default | Description |
|---|---|---|---|
-t, --target |
string | "" |
Target gateway: traefik, caddy, or nginx (optional if auto-detected or passed as positional argument) |
-c, --config |
string | "" |
Path to gateway config (traefik.toml, traefik.yaml, docker-compose.yaml, Caddyfile, nginx.conf, routewarden.json, or -) |
--format |
string | "" |
Explicit format: traefik-toml, traefik-yaml, traefik-labels, caddy, nginx, json |
--labels |
string | "" |
Direct Traefik Docker labels string (e.g. 'traefik.http.middlewares.warden...') |
--probe-path |
string | "" |
Custom endpoint path to verify blocked during --test |
--probe-ip |
string | "" |
Client IP to simulate via X-Forwarded-For to verify allowlist bypass during --test |
--port |
int | 8080 |
Local port to bind the gateway |
--version |
string | "" |
Target gateway container version tag (e.g. v3.3, 2.11.4, alpine) |
--plugin-version |
string | "" |
RouteWarden plugin version/tag/branch (e.g. v1.2.0, v1.1.0, main) |
--plugin-path |
string | "" |
Local path to RouteWarden plugin directory to mount for development |
-p, --print-config |
bool | false |
Print generated/wrapped gateway configuration before running |
--test |
bool | false |
Run automated live HTTP probe assertions against container then exit |
-n, --dry-run |
bool | false |
Generate config and show docker command without starting container |
-d, --detach |
bool | false |
Run container in background mode |
# Direct sensitive file access (blocked)
curl -i http://localhost:8080/.env
# Path traversal & double URL encoding evasion (blocked)
curl -i http://localhost:8080/static/%252e%252e/.env
# Query parameter inspection (blocked if checkQuery enabled)
curl -i "http://localhost:8080/search?file=secret.conf"
# Header smuggling injection (blocked if checkHeaders enabled)
curl -i -H "X-Forwarded-Uri: /.env" http://localhost:8080/api/dashboard
# Legitimate public endpoint (allowed downstream)
curl -i http://localhost:8080/robots.txt
# Client IP whitelisting test (simulating a request from whitelisted IP 192.168.1.50)
curl -i -H "X-Forwarded-For: 192.168.1.50" http://localhost:8080/.envSecurity Note: In local testing,
X-Forwarded-Forallows simulating whitelisted IPs without reconfiguring network interfaces. In production, edge reverse proxies (Cloudflare, AWS ALB, Traefik, NGINX) overwrite or sanitize untrusted client-supplied headers with the real TCP socket address.
Launch a lightweight, self-hosted web dashboard to visualize real-time security events, blocked probes, honeypot tarpit engagements, and attack analytics across your Traefik, Caddy, and NGINX instances.
The dashboard features:
- Zero-Config Docker Discovery: Reads container logs directly via the local Docker socket (
/var/run/docker.sock) to auto-detect and stream logs from running Traefik, Caddy, and NGINX gateways. - Log File Tailing: Tail local log files or wildcard patterns (e.g.
/var/log/routewarden/*.log) with automatic log rotation handling. - Real-Time Live Feed: Live WebSocket stream of blocked requests, client IPs, matched patterns, HTTP methods, and triggered response modes.
- Rich Visual Analytics: 24-hour attack trends, blocks per minute, top attacked endpoints, top offender IPs, response mode breakdown (block, tarpit, gzipBomb, silentDrop, fakeSuccess), and gateway distribution.
- Zero-Dependency Single Binary: The modern React SPA frontend is pre-compiled and embedded directly inside the
rwardenGo binary (go:embed). No Node.js runtime, no external databases, and no background services required. - Docker Ready: Runs as a lightweight standalone container or alongside your reverse proxies in
docker-compose.yml.
# 1. Start dashboard with Docker auto-discovery and open browser automatically
rwarden dashboard
# 2. Bind to a custom port without auto-opening the browser
rwarden dashboard --port 8080 --no-open
# 3. Tail one or more local RouteWarden log files
rwarden dashboard --log /var/log/routewarden.log
# 4. Tail wildcard patterns and multiple log sources simultaneously
rwarden dashboard --log "/var/log/routewarden/*.log" --log /var/log/nginx/access.log
# 5. Standalone file-only mode (disable Docker socket discovery)
rwarden dashboard --no-docker --log /var/log/routewarden.log
# 6. Customize memory retention (number of past events loaded)
rwarden dashboard --history 2500 --port 9090The official ghcr.io/routewarden/cli container image starts the dashboard by default, bound to 0.0.0.0:9090:
# Auto-discover gateway containers via Docker socket
docker run -d \
--name routewarden-dashboard \
--restart unless-stopped \
-p 9090:9090 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
ghcr.io/routewarden/cli:latest
# Or tail log files from a host volume
docker run -d \
--name routewarden-dashboard \
--restart unless-stopped \
-p 9090:9090 \
-v /var/log/routewarden:/logs:ro \
ghcr.io/routewarden/cli:latest \
dashboard --host 0.0.0.0 --no-docker --log "/logs/*.log"Deploy the dashboard alongside your existing reverse proxy infrastructure:
version: "3.8"
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
routewarden-dashboard:
image: ghcr.io/routewarden/cli:latest
container_name: routewarden-dashboard
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
command: ["dashboard", "--host", "0.0.0.0", "--no-open"]| Flag | Type | Default | Description |
|---|---|---|---|
--port |
int | 9090 |
Port to serve the dashboard web interface |
--host |
string | 127.0.0.1 |
Host address to bind (0.0.0.0 for Docker / remote access) |
--log |
string | "" |
Path or glob pattern to log file(s) to tail (repeatable) |
--no-docker |
bool | false |
Disable Docker daemon socket discovery |
--socket |
string | /var/run/docker.sock |
Path to Docker daemon Unix socket |
--history |
int | 1000 |
Number of events retained in memory and loaded on startup |
--no-open |
bool | false |
Do not automatically launch the system default browser |
The dashboard server exposes an HTTP API for external integrations, status checks, and alerting scripts:
| Endpoint | Method | Description |
|---|---|---|
/api/health |
GET |
Health check returning status, version, and active client count |
/api/events?n=500 |
GET |
Fetch the last n recorded security events as JSON |
/api/stats?hours=24 |
GET |
Aggregated analytics snapshot (rates, top IPs, top paths, response modes, gateway distribution) |
/api/sources |
GET |
List of active log sources (Docker containers & tailed files) and their statuses |
/ws/events |
GET |
Real-time WebSocket connection for live event streaming |
Stop and remove all running or detached RouteWarden sandbox containers:
CLI:
rwarden cleanup
# Or via sandbox subcommand:
rwarden sandbox cleanupDocker:
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/routewarden/cli:latest cleanupDisplay the current RouteWarden CLI version:
CLI:
rwarden version
rwarden --version
rwarden -vDocker:
docker run --rm ghcr.io/routewarden/cli:latest versionExample Output:
rwarden version 2.1.0
MIT License. See LICENSE for details.