Skip to content

API reference

The public surface of rocket_guard_rs 1.0.0. The full crate documentation is also in src/lib.rs (build it with cargo doc --open).

Fairing

GuardFairing

The Rocket fairing that screens every request and registers the refusal catchers. Attach it once on rocket::build():

rocket::build().attach(rocket_guard_rs::GuardFairing::new(config))

Attach with Kind::Ignite | Kind::Request | Kind::Response (the default Kind set the fairing declares): on_ignite registers the 400/403/413/429/500 catchers, on_request runs the IP gate, the stateful stage (dynamic bans, then rate limiting), and the path, query, and header views, stashing the verdict in request-local state, and on_response supports the 404 rewrite described below.

Constructors and builders:

Method Description
GuardFairing::new(config: DetectConfig) Build the fairing from an engine DetectConfig. The body cap starts at config.max_full_scan_bytes
GuardFairing::with_defaults() Build the fairing with default_config()
.with_body_cap(body_cap: usize) Replace the body buffering cap, in bytes. A body larger than the cap is refused with 413 rather than forwarded unscanned
.with_ip_gate(ip_gate: IpGateConfig) Install the global IP gate (see below)
.with_rate_limiting(limiter: RateLimiter) Install the rate limiter (see below)
.with_ip_banning(manager: IpBanManager, config: IpBanConfig) Install the dynamic ban store and the auto-ban engine (see below)

Routes without a guard argument are only scanned, not blocked: in Rocket, protection is per-route, and that is what the guard argument is for. When the verdict is a block (a threat, an IP-gate denial, a live ban, an auto-ban firing, or a rate-limit crossing) and the request would otherwise answer 404, the fairing rewrites the 404 to that verdict's family shape so probe traffic never reveals route inventory.

The IP gate: IpGateConfig

Built with IpGateConfig::new(whitelist, blacklist, exempt_ips), which fails closed on an invalid entry (IpGateError names the list and the entry):

use rocket_guard_rs::{GuardFairing, IpGateConfig};

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

The gate runs in on_request before the metadata scan, on the request's client IP: a blacklisted IP - or an IP a non-empty whitelist matches neither directly nor through exempt_ips - is refused with 403 Forbidden (the Forbidden body), both through the guards and through the 404 rewrite for unrouted paths. A request whose client IP is unknown (Rocket's local test client, for example) is not attributed: the gate does not run and the scan runs unconditionally.

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 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 stateful stages (GuardFairing::with_rate_limiting, GuardFairing::with_ip_banning) skip exactly what the reference skips for a whitelist match (is_whitelisted || is_exempt): rate limiting, violation counting, and banning. Detection never skips anything.

The rate limiter: RateLimiter

use rocket_guard_rs::{GuardFairing, RateLimitConfig, RateLimiter};

let limiter = RateLimiter::new(RateLimitConfig {
    enable_rate_limiting: true,
    rate_limit: 30,
    rate_limit_window: 10,
    ..RateLimitConfig::default()
})
.expect("valid config");
let fairing = GuardFairing::with_defaults().with_rate_limiting(limiter);

The limiter's constructor fails closed on a zero limit or window. Installed with GuardFairing::with_rate_limiting, it runs in on_request after the IP gate and the ban stage, before the metadata scan: a crossing is refused with 429 Too Many Requests carrying Retry-After: <window seconds>. With enable_rate_limit_auto_ban on and IP banning configured, every crossing counts one rate_limit violation toward the auto-ban engine; the response stays 429 and the ban bites on the next request. Requests without a client IP cannot be attributed and are not rate limited; detection still screens them. The limiter is shared with the managed engine state and clone-shares its window store, so out-of-band handles (stats, admin resets) work alongside the installed fairing.

The ban stage: IpBanManager + IpBanConfig

use rocket_guard_rs::{GuardFairing, IpBanConfig, IpBanManager, ThreatBanEntry};

let manager = IpBanManager::new();
let config = IpBanConfig::new(
    true,
    10,
    3600,
    [("sqli", ThreatBanEntry { threshold: 3, duration: 1800 })],
)
.expect("valid config");
let fairing = GuardFairing::with_defaults().with_ip_banning(manager, config);

The config constructor fails closed on an invalid threat_ban_config entry. Installed with GuardFairing::with_ip_banning, the ban check runs in on_request before the limiter: a live ban is refused with 403 Forbidden (IP address banned) and banned traffic never consumes rate budget. Every detected threat counts its categories per client IP (the reference pipeline's suspicious-activity stage), and a crossed threat_ban_config entry or the flat auto_ban_threshold bans on the spot, answering 403 Forbidden (IP has been banned); without a crossing the block keeps the 400 Bad Request (Suspicious activity detected) shape. config.enable_ip_banning = false counts violations but never bans. Whitelisted and exempt IPs are never counted, so they can never be auto-banned. The store pair is shared with the managed engine state and clone-shares its stores.

Catchers

guard_catchers() -> Vec<Catcher> returns the 400/403/413/429/500 catchers that render refusals as the ecosystem's plain-text error shape (the throttled shape carries the Retry-After header). The fairing registers them itself, skipping any status the application already registered a catcher for (Rocket treats same-code catchers at the same base as a fatal collision).

Guards

BlockGuard

A request guard for routes without a body argument:

use rocket::get;
use rocket_guard_rs::BlockGuard;

#[get("/health")]
fn health(_guard: BlockGuard) -> &'static str {
    "hello"
}

It enforces the verdict stashed by the fairing: a threat answers 400, a gate denial, live ban, or auto-ban answers 403, a rate-limit crossing answers 429 (with Retry-After), and a failed check answers 500. Fail-secure rule: if the guard cannot find a verdict, the security system is not running, and the request is refused rather than passed uninspected.

GuardBody

The scanned request body, usable as a data guard. Buffering, scanning, and handing the body to the handler are fused into one data guard because Rocket's Data is a one-shot stream:

use rocket::post;
use rocket_guard_rs::GuardBody;

#[post("/submit", data = "<body>")]
fn submit(body: GuardBody) -> Vec<u8> {
    body.into_inner()
}

As a data argument (data = "<body>"), it refuses the request on any of the fairing's metadata views and scans the body itself. The body is buffered up to the cap configured on GuardFairing::with_body_cap (default: the engine's full-scan cap, 262,144 bytes). Rocket's own per-guard limits still apply to whatever the handler does with the scanned bytes afterwards.

Methods:

Method Description
.into_inner() -> Vec<u8> Consume the guard and return the scanned bytes
.as_slice() -> &[u8] Borrow the scanned bytes

GuardBodyError

The error type produced when the body cannot be buffered or scanned.

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 guard_core_engine::detect. 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

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 by GuardBody, capped

The HTTP method is not scanned.

Responses

Situation Status Body
The IP gate denies the client IP 403 Forbidden Forbidden
A live ban on the client IP 403 Forbidden IP address banned
Rate limit crossed 429 Too Many Requests (+ Retry-After: <window>) Too many requests
Engine flags a view 400 Bad Request Suspicious activity detected
Engine flags a view and a crossed auto-ban threshold bans on the spot 403 Forbidden IP has been banned
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).

Constants

Re-exported refusal message bodies:

Constant Value
BLOCKED_MESSAGE "Suspicious activity detected"
FORBIDDEN_MESSAGE "Forbidden"
BANNED_MESSAGE "IP address banned"
ACTIVITY_BANNED_MESSAGE "IP has been banned"
RATE_LIMITED_MESSAGE "Too many requests"
OVERSIZE_MESSAGE "Payload too large"
FAILURE_MESSAGE "Security check failed"

Engine re-exports

DetectConfig, DetectVerdict, and Threat are re-exported from guard_core_engine::detect.

IpGateConfig, IpGateDecision, IpGateDenial, IpGateError, and IpGateVerdict are re-exported from guard_core_engine::ip_gate. A DetectVerdict carries is_threat, a threat_score, and the list of Threat findings (regex or semantic).

RateLimiter, RateLimitConfig, RateLimitConfigError, and RateLimitDecision are re-exported from guard_core_engine::rate_limit.

IpBanManager, IpBanConfig, IpBanConfigError, BanError, BanRecord, Clock, ResolvedBan, ThreatBanEntry, and ViolationCounters are re-exported from guard_core_engine::ip_ban.