Skip to content

API reference

The public surface of axum_guard_rs 1.0.0. The full crate documentation is also in src/lib.rs (build it with cargo doc --open). Most behavior is defined by tower-guard-rs and re-exported here.

Guard layer

with_guard

use axum::Router;
use axum_guard_rs::{DetectConfig, with_guard};

let config = DetectConfig {
    max_content_length: 10_000,
    max_full_scan_bytes: 262_144,
    preserve_attack_patterns: true,
    semantic_threshold: 0.7,
    threat_score_threshold: 1.0,
};

let app: Router = Router::new().layer(with_guard(config));

with_guard(config: DetectConfig) -> GuardLayer builds the Guard layer for an axum Router. Apply it with Router::layer (every registered route) or Router::route_layer (routes only, skipping the fallback). Chain GuardLayer::with_body_cap to change the body buffering cap:

let layer = axum_guard_rs::with_guard(axum_guard_rs::default_config())
    .with_body_cap(1_048_576);
let app = Router::new().layer(layer);

A body larger than the cap is rejected with 413 Payload Too Large rather than forwarded unscanned. A cap of 0 rejects every request that carries a non-empty body.

The IP gate: with_ip_gate + client_ip_layer

The global IP gate from tower-guard-rs: whitelist, blacklist, and exempt_ips, parsed once at construction (invalid entry is a config error, fail closed) and evaluated before detection:

use axum_guard_rs::{IpGateConfig, client_ip_layer, default_config, with_guard};

let gate = IpGateConfig::new(
    [] as [&str; 0],
    ["203.0.113.9"],
    ["198.51.100.7", "198.51.100.16/28"],
)
.expect("valid lists");

let app: Router = Router::new()
    .layer(with_guard(default_config()).with_ip_gate(gate))
    .layer(client_ip_layer());

A blacklisted IP, or an IP a non-empty whitelist matches neither directly nor through exempt_ips, is denied with 403 Forbidden (Forbidden body) before detection. client_ip_layer() copies axum's ConnectInfo<SocketAddr> (present when the router is served with into_make_service_with_connect_info::<SocketAddr>()) into the GuardClientIp extension the gate reads; apply it after the guard layer (axum runs the last-added layer first). Requests without connect info are not attributed: the gate does not run and detection still screens them.

exempt_ips vs whitelist. exempt_ips is noise reduction for known-friendly automation (monitoring probes, VPN egress, a partner's server), not immunity: it sets the same skip state a whitelist match sets (IpGateDecision in the request extensions) but never adds a deny path and never opens the whitelist gate. The blacklist, bans-style checks, and detection still apply to exempt IPs - an attack payload from an exempt IP is still 400 Suspicious activity detected. The Rust family ships no rate limiter, user-agent filter, cloud-provider blocker, or violation counter yet; a stage that lands later must skip exactly what the reference skips for a whitelist match and never skip detection.

Configuration

default_config()

Returns the reference default DetectConfig:

Knob Value
max_content_length 10_000
max_full_scan_bytes 262_144
preserve_attack_patterns true
semantic_threshold 0.7
threat_score_threshold 1.0

DetectConfig

Re-exported from the engine (via tower-guard-rs). Fields:

Field Type Meaning
max_content_length usize Semantic budget and truncation budget
max_full_scan_bytes usize Preprocessor full-scan cap (also the default body cap)
preserve_attack_patterns bool Keep attack patterns in the processed view
semantic_threshold f64 Semantic analysis threshold
threat_score_threshold f64 Threat score threshold for a verdict

Re-exports

From tower-guard-rs:

Item Kind
GuardLayer The tower layer with_guard returns
GuardService The tower service the layer produces
GuardBody Body wrapper the service answers with
BoxError Boxed error type used by the guard body
default_config Reference default DetectConfig
DetectConfig, DetectVerdict, Threat Engine types from guard_core_engine::detect
BLOCKED_MESSAGE, OVERSIZE_MESSAGE, FAILURE_MESSAGE, FORBIDDEN_MESSAGE Refusal message bodies ("Suspicious activity detected", "Payload too large", "Security check failed", "Forbidden")
GuardClientIp Extension type the IP gate reads the client IP from
IpGateConfig, IpGateDecision, IpGateDenial, IpGateError, IpGateVerdict Engine types from guard_core_engine::ip_gate

Behavior

What it inspects

One engine call per request view:

Request part Engine context Notes
Path url_path Skipped for /
Query string query_param Skipped when empty
Header values header Skips sec-* and the negotiation/routing headers
Body request_body Buffered first, capped

The HTTP method is not scanned.

Responses

Situation Status Body
Engine flags a view 400 Bad Request Suspicious activity detected
Body exceeds the cap 413 Payload Too Large Payload too large
Body read error or engine panic 500 Internal Server Error Security check failed

The adapter is fail-secure: any failure to complete the security check answers 500, never an uninspected passthrough. Engine panics are caught with catch_unwind (note that panic = "abort" in a release profile disables that recovery).