Configuration

dvarapala serve --config <file> reads a TOML file. Every key can also be set, or overridden, by an environment variable: DVARAPALA__ followed by the key path with __ between parts, for example DVARAPALA__RATE_LIMIT__RATE=5 for rate_limit.rate. Unknown keys are an error. The log level comes from RUST_LOG (default info).

A commented example is in examples/dvarapala.toml.

Durations are strings: "<n>ms", "<n>s", "<n>m" or "<n>h", for example "500ms", "10s" or "24h".

Top level

Top-level keys go above the first [section] in the file.

Key Type Default Description
mode "reverse" or "forward" "reverse" "forward" runs an egress forward proxy (RFC 0007), see [forward]
listen address "0.0.0.0:8080" Proxy listen address
upstream URL required in reverse mode Upstream base URL, http:// or https://; ignored in forward mode
upstream_protocol "http1", "http2" or "auto" "http1" Protocol to the upstream (RFC 0004): http2 is h2c with prior knowledge for http:// and ALPN h2 for https:// (an upstream that cannot speak it gets 502); auto lets ALPN choose for https:// and keeps HTTP/1.1 for http://
trusted_proxies list of CIDRs [] Peers allowed to set X-Forwarded-For; others are keyed by their own address

[metrics]

Key Type Default Description
listen address "127.0.0.1:9090" Prometheus endpoint; empty turns it off

[rate_limit]

Token bucket per client (RFC 0001).

Key Type Default Description
enabled bool true Turn the limiter off
rate integer 20 Tokens refilled per second per client
burst integer 40 Bucket size
max_clients integer 100000 Table capacity before the least recently seen client is evicted; at least 1

[detection]

Adaptive detection (RFC 0002). Starts in observe mode: logs and metrics only.

Key Type Default Description
enabled bool true Turn detection off entirely
mode "observe" or "enforce" "observe" observe logs only, enforce changes verdicts
window duration "1s" Window length
prefix_v4 integer 32 IPv4 prefix length for aggregation; shorten (for example 24) to group subnets
prefix_v6 integer 64 IPv6 prefix length for aggregation; shorten (for example 48) to group sites
sketch_width integer 2048 Count-min columns
sketch_depth integer 4 Count-min rows (hash functions)
top_k integer 64 Heavy hitters tracked
ewma_alpha float 0.05 Baseline learning rate per window
warmup_windows integer 300 Windows before the baseline can alarm
z_threshold float 4.0 Rate z-score that marks a window anomalous
entropy_buckets integer 1024 Hashed buckets for source entropy
entropy_drop float 0.5 Relative entropy drop that marks a window anomalous
trigger_windows integer 3 Anomalous windows in a row to enter Attack
cooldown_windows integer 30 Normal windows to leave Cooldown
attack_rate_factor float 0.25 Bucket rate multiplier while in Attack

[ml]

Optional tree model scoring, in builds with the ml feature (off by default).

Key Type Default Description
model path unset XGBoost JSON model; unset turns scoring off
threshold float 0.9 Score above which Allow becomes Throttle

[tls]

HTTPS on the listener (RFC 0003), in builds with the tls feature (on by default). Set cert and key together to turn TLS on; one without the other is an error. SIGHUP reloads the pair; a bad file keeps the old one. ALPN offers h2 and http/1.1.

Key Type Default Description
cert path unset PEM certificate chain
key path unset PEM private key
handshake_timeout duration "10s" Handshakes slower than this are dropped
max_handshakes integer 1024 Handshakes in progress at once; new connections over it are closed. At least 1

[upstream_tls]

Verification of https:// upstreams. There is no switch to turn verification off.

Key Type Default Description
ca_file path system roots PEM CA bundle to trust instead of the system roots
server_name string upstream host Name to send in SNI and verify the certificate against

[http2]

HTTP/2 listener limits (RFC 0004), applied to every HTTP/2 client connection, cleartext (h2c) or TLS.

Key Type Default Description
max_concurrent_streams integer 100 Streams open at once per connection; more are refused with REFUSED_STREAM. At least 1
max_pending_reset_streams integer 20 Streams the client resets before they are served; over it, GOAWAY (rapid reset)
max_local_error_resets integer 1024 Streams the server resets for client errors before it closes the connection
max_header_list_size integer 16384 Bytes of headers per request, CONTINUATION frames included
keepalive_interval duration "30s" PING interval; "0s" turns keepalive off
keepalive_timeout duration "10s" Close the connection when a PING is not answered in time

[upgrade]

WebSocket and other HTTP/1.1 upgrades (RFC 0005). The upgrade request goes through the rate limit, detection and model score like any request. On an upstream 101 the proxy copies bytes both ways until either side closes; it never parses WebSocket frames. Upgrades need HTTP/1.1 on both sides: with upstream_protocol = "http2" they are stripped, and WebSocket over HTTP/2 or HTTP/3 is not supported.

Key Type Default Description
protocols list of strings ["websocket"] Upgrade tokens passed to the upstream, case-insensitive; others are stripped. [] turns upgrades off
max_tunnels integer 10000 Open tunnels at once; over it, the upgrade gets 503 before it reaches the upstream. At least 1
idle_timeout duration "300s" Close a tunnel with no bytes in either direction for this long. Above 0
max_lifetime duration "24h" Close any tunnel after this long, so clients reconnect through the current limits; "0s" turns it off

An idle tunnel costs about 19 KB in the proxy (two 8 KB copy buffers plus connection state), so the default max_tunnels bounds tunnels to about 200 MB. Shutdown closes open tunnels and waits for them within the 30 s drain.

[forward]

Forward proxy mode (mode = "forward", RFC 0007): egress rate compliance for calls to third-party APIs. Clients set HTTP_PROXY (or their HTTP client’s proxy) to listen. Only listed destinations are reachable, each shaped to its own rate shared by every client: a request waits for its slot, or gets a local 429 with Retry-After when the slot is more than max_wait away. The proxy never sends above the rate. When a destination answers 429 or 503 with Retry-After (seconds), it pauses for that long, capped by max_pause.

[rate_limit], [detection] and [ml] do not apply in forward mode: the callers are your own services. [upstream_tls] verifies https:// destinations, [upgrade] limits CONNECT tunnels (max_tunnels, idle_timeout, max_lifetime), [tls] and [http2] apply to the listener as in reverse mode.

Key Type Default Description
clients list of CIDRs ["127.0.0.0/8", "::1/128"] Peers allowed to use the proxy; others get 403
connect bool true Allow CONNECT to listed destinations; false accepts absolute-form requests only

Each [[forward.destinations]] (at least one):

Key Type Default Description
origin URL required https://host[:port] or http://host[:port], no path. Matched on scheme, host and port; CONNECT matches host and port
name string the host Label in logs and metrics
rate number required Requests (or new CONNECT tunnels) per second, at least 0.001
burst integer 1 Requests that may go at once after an idle period; at least 1
max_concurrent integer 0 (off) Requests or tunnels in flight at once; a request waits for a place within max_wait, then gets 429 with Retry-After: 1
max_queue integer 100 Requests waiting at once; 0 refuses instead of waiting
max_wait duration "2s" Longest wait for a slot or a place
max_pause duration "60s" Cap on a destination’s Retry-After; "0s" ignores it

Two ways in. An absolute-form request (GET https://host/path sent to the proxy as plain HTTP) is sent by the proxy itself, opening TLS for https:// destinations, so every request is shaped; keep that hop on a trusted network. Most clients send https:// URLs through a proxy as CONNECT instead: the tunnel keeps TLS end to end and takes one slot per tunnel, not per request. Hop-by-hop headers (Proxy-Authorization included) are removed and no X-Forwarded-For is added. A waiting request costs about 5.5 KB.

gRPC

Requests with content-type: application/grpc or application/grpc+<codec> get local errors in gRPC form: HTTP 200 with grpc-status and grpc-message in a trailers-only response.

Cause grpc-status
Rate limit or model score (Throttle) 8 RESOURCE_EXHAUSTED, with retry-after and grpc-retry-pushback-ms
Detection Drop 7 PERMISSION_DENIED
Upstream unreachable 14 UNAVAILABLE

Use upstream_protocol = "http2" for gRPC upstreams.

Metrics

Served on metrics.listen at /metrics.

Metric Description
dvarapala_requests_total{verdict,protocol} Requests by verdict and client protocol (http1, http2)
dvarapala_limiter_clients Clients in the limiter table
dvarapala_limiter_evictions_total Evictions; a steady rise means max_clients is too small or a spoofed flood
dvarapala_upstream_errors_total Upstream (or forward destination) connect or response failures
dvarapala_grpc_local_responses_total{status} gRPC errors answered locally: resource_exhausted, permission_denied, unavailable
dvarapala_tunnels_open{protocol} Open tunnels (protocol="connect" for forward mode CONNECT); compare with upgrade.max_tunnels
dvarapala_tunnels_total{protocol,end} Closed tunnels by end: client or upstream (that side closed first), idle, lifetime, shutdown
dvarapala_tunnel_bytes_total{direction} Bytes copied: in from clients, out from the upstream
dvarapala_tunnels_rejected_total Upgrades refused over upgrade.max_tunnels; logged as tunnel limit reached at most once a second
dvarapala_egress_requests_total{destination,result} Forward mode (RFC 0007): sent, delayed (sent after a wait), refused (local 429, logged as egress refused at most once a second), denied (403; destination="none" when no destination is listed for the target)
dvarapala_egress_wait_seconds{destination} Histogram of the wait before a delayed send
dvarapala_egress_queue{destination} Requests waiting now; compare with max_queue
dvarapala_egress_in_flight{destination} Requests or CONNECT tunnels in flight; compare with max_concurrent
dvarapala_egress_provider_pauses_total{destination} Provider 429 or 503 with Retry-After that paused the destination
dvarapala_http2_goaway_total{reason} HTTP/2 connections closed with GOAWAY: flood (rapid reset, error resets, header floods), protocol (other protocol errors)
dvarapala_detection_state 0 Normal, 1 Suspect, 2 Attack, 3 Cooldown
dvarapala_detection_rate_zscore Current window z-score
dvarapala_detection_source_entropy Current window entropy, normalized to [0, 1]
dvarapala_detection_heavy_hitters Prefixes in the top-k above the per-client limit
dvarapala_detection_transitions_total{from,to} State changes
dvarapala_ml_score Histogram of model scores (feature ml)
dvarapala_tls_handshakes_total{result} Handshakes by ok, failed, timeout, rejected
dvarapala_tls_handshakes_in_progress Handshakes running now
dvarapala_tls_resumed_total Resumed sessions
dvarapala_tls_cert_expiry_timestamp_seconds notAfter of the served certificate, Unix seconds