Skip to content

SecurityConfig Reference

SecurityConfig is a Pydantic BaseModel that controls all guard-core behavior. Adapter developers should expose relevant fields to their users while keeping internal fields (agent, dynamic rules) as implementation details.

Core Settings

Field Type Default Description
passive_mode bool False Log-only mode. Logs and emits events but never blocks.
exclude_paths list[str] See below Paths excluded from all security checks.
custom_error_responses dict[int, str] {} Override error messages for specific HTTP status codes.
enforce_https bool False Redirect HTTP requests to HTTPS globally.
custom_request_check Callable \| None None Global async function for custom request validation.
custom_response_modifier Callable \| None None Global async function to modify responses.
route_resolution_strict bool False Block with 500 when the adapter reports it could not resolve the route, instead of running the pipeline with no per-route config. Also turns requests to paths the app does not serve into 500s rather than 404s. See Reporting a Failed Match.
on_error Callable[[str, BaseException, dict], None] \| None None Best-effort callback invoked when a middleware/agent step fails, receiving (stage, exception, context). stage is one of agent_init, geoip, transport_send, encryption. Also forwarded to AgentConfig.on_error when the agent is enabled; see Agent / Telemetry.

Default exclude_paths: ["/docs", "/redoc", "/openapi.json", "/openapi.yaml", "/favicon.ico", "/static"]

Adapter Exposure

All core settings should be exposed to end users. passive_mode is particularly useful for deployment rollouts.


Proxy Configuration

Field Type Default Description
trusted_proxies list[str] [] Trusted proxy IPs or CIDR ranges for X-Forwarded-For.
trusted_proxy_depth int 1 Number of proxies in the X-Forwarded-For chain.
trust_x_forwarded_proto bool False Trust X-Forwarded-Proto header for HTTPS detection.

Validators:

  • trusted_proxies: Each entry is validated as a valid IP address or CIDR range.
  • trusted_proxy_depth: Must be >= 1.

Your app server must not pre-resolve the client itself

Leaving trusted_proxies unset only means "X-Forwarded-For is never trusted" if your ASGI/WSGI server isn't already applying that header before guard-core runs. uvicorn's default proxy_headers=True (and equivalent settings in Gunicorn/Hypercorn) does exactly that. See Deployment Prerequisite for the fix.


IP Management

Field Type Default Description
whitelist list[str] \| None None Allowed IPs/CIDRs. None disables (allow all).
blacklist list[str] [] Blocked IPs/CIDRs.
whitelist_countries list[str] [] Allowed countries. Non-empty = only listed pass (unknown blocked). Overrides blocked_countries.
blocked_countries list[str] [] Country codes always blocked.
blocked_user_agents list[str] [] Regex patterns for blocked user agents.
enable_ip_banning bool True Enable automatic IP banning.
auto_ban_threshold int 10 Suspicious requests before auto-ban.
auto_ban_duration int 3600 Ban duration in seconds.

Validators:

  • whitelist and blacklist: Each entry validated as a valid IP or CIDR range via ipaddress.ip_address() / ip_network().

Whitelist Semantics

whitelist=None means "no whitelist" (all IPs pass). whitelist=[] means "empty whitelist" (no IPs pass). Adapter developers should document this distinction.


Per-Category Bans

threat_ban_config lets each detection category carry its own threshold and duration. The pipeline tracks per-category counts in suspicious_request_counts, then walks the matched categories of the current detection. The first category whose own count reaches its threshold triggers a category-tagged ban; if no per-category entry matches, the flat auto_ban_threshold / auto_ban_duration fallback fires off the total count instead.

Field Type Default Description
threat_ban_config dict[str, ThreatBanConfig] {} Per-category ban policy. Validator rejects unknown keys.

When to use:

  • You want SQL injection detections to be a single-strike ban for a week, but XSS detections to be a 3-strike ban for an hour.
  • You want to keep the existing flat threshold for any category you do not name.
  • You want the audit log reasons to disambiguate which category triggered the ban ("penetration_attempt:sqli" vs the flat "penetration_attempt").
from guard_core.models import SecurityConfig, ThreatBanConfig

config = SecurityConfig(
    auto_ban_threshold=10,
    auto_ban_duration=3600,
    threat_ban_config={
        "sqli": ThreatBanConfig(threshold=1, duration=604800),
        "xss": ThreatBanConfig(threshold=3, duration=86400),
    },
)

See Ban Configuration for the ThreatBanConfig model and the full fall-through rule.


Global Behavior Rules

global_behavior_rules applies behavior rules to every route without requiring decorators. The merged rules are run alongside any decorator-specified rules. The most common use is service-wide 404-noise correlation, but the same shape supports usage, frequency, and return_pattern rules.

Field Type Default Description
global_behavior_rules list[BehaviorRuleConfig] [] Behavior rules merged into every route.

When to use:

  • You want a global "ban after 20 404s in 5 minutes" rule that does not require touching every route.
  • You want detection-correlated thresholds — correlate_with_detection=True halves the threshold (floor 1) when the IP has any positive suspicious_request_counts entry, so probing that already triggered a regex hit gets banned faster.
  • You want a service-wide frequency or usage cap for any caller, regardless of which route they hit.
from guard_core.models import BehaviorRuleConfig, SecurityConfig

config = SecurityConfig(
    global_behavior_rules=[
        BehaviorRuleConfig(
            rule_type="return_pattern",
            threshold=20,
            window=300,
            pattern="status:404",
            action="ban",
            ban_duration=3600,
            correlate_with_detection=True,
        ),
    ],
)

See Behavior Rules for the full field reference.


Detection Exclusions

These fields opt request components out of penetration detection. The header set is merged with a hardcoded default that already excludes host, user-agent, accept, accept-encoding, connection, origin, referer, all sec-fetch-*, and all sec-ch-ua* headers. enabled_detection_categories narrows the regex scan to a subset of the 18 known categories; custom user patterns always run regardless.

Field Type Default Description
excluded_detection_headers set[str] set() Header names skipped by detection. Merged with the hardcoded default list.
excluded_detection_params set[str] set() Query parameter names skipped by detection.
excluded_detection_body_fields set[str] set() Top-level JSON body keys skipped by detection.
enabled_detection_categories set[str] full ALL_DETECTION_CATEGORIES Categories scanned for. Validator rejects unknown labels.

When to use:

  • A first-party endpoint accepts JSON containing literals (Markdown source, code blobs, URL-shaped query params) that look like attacks but are not.
  • A regression in one category's regex is producing false positives faster than you can write a fix — disable the category temporarily.
  • A privacy-sensitive header value should not be scanned at all.
  • You want different routes to have different opt-outs — pair this with @security.detection_exclusion(...) on the route.
from guard_core.models import SecurityConfig

config = SecurityConfig(
    excluded_detection_params={"q", "search", "filter"},
    excluded_detection_body_fields={"description", "markdown"},
    enabled_detection_categories={"sqli", "xss", "cmd_injection", "ssrf"},
)

IP Lifecycle Controls

These fields tune cold-start and horizontal-scale behaviour for the geo-IP and cloud-IP subsystems. They are inert by default — only adjust if you have a specific cold-start or scale-out problem.

Field Type Default Description
lazy_init bool True When True (default), run the IPInfo MMDB download and cloud-IP provider fetches as a background task during startup instead of awaiting them inline, so app boot never blocks on multi-second network calls. Cloud and geo layers are inert until the task completes. Set to False to await them inline for synchronous-init guarantees.
geo_ip_db_max_age int 86400 Maximum age in seconds for the IPInfo MMDB before re-download. Range 3600 - 604800.
cloud_ip_store CloudIpStoreProtocol \| None None Pluggable cloud-IP backend. None uses the in-memory default; auto-upgraded to Redis when Redis is enabled.

When to use:

  • lazy_init=True to keep startup non-blocking when IPInfo MMDB or cloud-IP provider fetches are slow. The background warmup runs concurrently with normal request handling; cloud-provider blocking and geo checks become active once the background task finishes. Rate limiting, IP banning, pattern detection, and other layers remain fully active throughout the warmup window. lazy_init only takes effect when Redis is enabled and the adapter calls initialize_redis_handlers() from its own startup hook (for example fastapi-guard's lifespan integration) — see Provider Status below for the accessor that lets a Kubernetes/ALB warmup probe (or any health endpoint) tell when that window has closed.
  • geo_ip_db_max_age to tighten or loosen the IPInfo refresh cadence — match it to your IPInfo plan's update frequency.
  • cloud_ip_store to point multiple horizontally-scaled instances at a single pre-populated Redis namespace, skipping per-instance cloud-IP cold starts.
from guard_core.handlers.cloud_ip_stores import RedisCloudIpStore
from guard_core.handlers.redis_handler import RedisManager
from guard_core.models import SecurityConfig

config = SecurityConfig(
    lazy_init=True,
    geo_ip_db_max_age=43200,
)

shared_store = RedisCloudIpStore(RedisManager(config))
config_with_shared_store = SecurityConfig(cloud_ip_store=shared_store)

Provider Status

cloud_handler.get_status() (the module-level singleton) and your IPInfoManager instance's get_status() report per-provider readiness, the last successful refresh timestamp, and a cheap entry count. HandlerInitializer is adapter-internal — its get_initialization_status() combines both into one payload, and adapters expose that combined payload as their status surface (fastapi-guard: SecurityMiddleware.get_initialization_status(), or add_status_route(app)GET /_guard/status).

Cloud-only status, callable anywhere:

from guard_core.handlers.cloud_handler import cloud_handler

cloud_status = cloud_handler.get_status()
# {
#     "AWS": {"ready": True, "last_refreshed": datetime(...), "entries": 3421},
#     "GCP": {"ready": False, "last_refreshed": None, "entries": 0},
#     ...
# }

Geo-IP status — call get_status() on the IPInfoManager instance you passed in as geo_ip_handler (there is no module singleton: the manager is token-gated, so it is instantiated per app, not at import time):

geo_status = ip_info_manager.get_status()
# {"ready": True, "last_refreshed": datetime(...), "entries": 494}

Combined cloud + geo-IP payload, for a warmup probe or health endpoint — read it through your adapter rather than reconstructing HandlerInitializer yourself (fastapi-guard):

from guard.status import add_status_route

add_status_route(app, path="/_guard/status")  # GET /_guard/status -> combined payload
# or, in-process: security_middleware.get_initialization_status()
# {
#     "cloud_providers": { ...as above... },
#     "geo_ip": { ...as above... },
# }

geo_ip is None when no geo_ip_handler is configured. A custom geo_ip_handler that does not implement get_status() still reports ready (from the required is_initialized property) with last_refreshed/entries as placeholders. This is synchronous, dependency-free, and cheap enough to poll from a warmup probe or health endpoint — it is exactly what to wire up for the "cannot tolerate any inert window" case above.

See Cloud IP Store for the protocol contract and the Redis namespace migration note.


Geolocation

Field Type Default Description
geo_ip_handler GeoIPHandler \| None None Custom geolocation handler implementing the protocol.
ipinfo_token str \| None None Deprecated. IPInfo API token.
ipinfo_db_path Path \| None data/ipinfo/country_asn.mmdb Deprecated. Path to IPInfo database.

Model validator: If blocked_countries or whitelist_countries are set, geo_ip_handler must be provided (or ipinfo_token for backward compatibility). Raises ValueError otherwise.


Rate Limiting

Field Type Default Description
enable_rate_limiting bool True Master switch for rate limiting.
rate_limit int 10 Maximum requests per window (global).
rate_limit_window int 60 Window duration in seconds (global).
endpoint_rate_limits dict[str, tuple[int, int]] {} Per-endpoint overrides {path: (limit, window)}.

Cloud Provider Blocking

Field Type Default Description
block_cloud_providers set[str] \| None None Providers to block. A bare name ("AWS", "GCP", "Azure") blocks the whole provider; a region carve-out ("GCP:!us-central1") blocks the provider except that region. Region scoping is supported for GCP and AWS.
cloud_ip_refresh_interval int 3600 Seconds between IP range refreshes (60-86400).

Validator: each entry is kept only if the part before an optional :!region suffix is one of {"AWS", "GCP", "Azure"}; an entry that fails this check is dropped, not the whole set.


Security Headers

Field Type Default Description
security_headers dict[str, Any] \| None See below Security headers configuration dict.

Default structure:

{
    "enabled": True,
    "hsts": {"max_age": 31536000, "include_subdomains": True, "preload": False},
    "csp": None,
    "frame_options": "SAMEORIGIN",
    "content_type_options": "nosniff",
    "xss_protection": "1; mode=block",
    "referrer_policy": "strict-origin-when-cross-origin",
    "permissions_policy": "geolocation=(), microphone=(), camera=()",
    "custom": None,
}

CORS

Field Type Default Description
enable_cors bool False Enable CORS header injection.
cors_allow_origins list[str] ["*"] Allowed origins.
cors_allow_methods list[str] ["GET", "POST", ...] Allowed HTTP methods.
cors_allow_headers list[str] ["*"] Allowed request headers.
cors_allow_credentials bool False Allow credentials in CORS requests.
cors_expose_headers list[str] [] Headers exposed in CORS responses.
cors_max_age int 600 Preflight cache duration in seconds.

Redis

Field Type Default Description
enable_redis bool True Master switch for Redis.
redis_url str \| None "redis://localhost:6379" Redis connection URL.
redis_prefix str "guard_core:" Key prefix for namespace isolation.
redis_socket_connect_timeout float \| None 2.0 Seconds to wait establishing a TCP connection. Must be positive; None disables (blocks indefinitely on a partitioned Redis).
redis_socket_timeout float \| None 2.0 Seconds to wait on a read/write before raising. Must be positive; None means no timeout.
redis_health_check_interval int 30 Seconds between pooled-connection health checks. 0 disables.
redis_max_connections int \| None None Cap on the connection pool size. None uses redis-py's default.
redis_retries int 1 Retries (with exponential backoff) on transient connection/timeout errors. 0 disables.
redis_fail_open bool False On Redis outage, fail_secure governs by default. Set True to skip the failing check and let the request through, treating Redis outages as an availability concern distinct from other check failures.

Detection Engine

Field Type Default Range Description
enable_penetration_detection bool True N/A Master switch for threat detection.
detection_compiler_timeout float 2.0 0.1 - 10.0 Timeout for pattern compilation/matching (s).
detection_max_content_length int 10000 1000 - 100000 Maximum content length for detection.
detection_preserve_attack_patterns bool True N/A Preserve attack patterns during truncation.
detection_semantic_threshold float 0.7 0.0 - 1.0 Threshold for semantic attack detection.
detection_anomaly_threshold float 3.0 1.0 - 10.0 Std deviations slower than average to flag an anomaly (never faster).
detection_slow_pattern_threshold float 0.1 0.01 - 1.0 Seconds to consider a pattern slow.
detection_monitor_history_size int 1000 100 - 10000 Recent metrics to keep in history.
detection_max_tracked_patterns int 1000 100 - 5000 Maximum patterns to track for performance.
detection_max_body_inspect_bytes int 262144 1024 - 10485760 Body size cap read/scanned for detection; distinct from detection_max_content_length and max_request_size.
detection_threat_score_threshold float 1.0 0.0 - 10.0 Anomaly/threat score required to flag a request.
detection_scan_body bool True N/A Scan the request body during detection; False restricts detection to path/query/headers.

Logging

Field Type Default Description
log_suspicious_level "INFO" \| "DEBUG" \| "WARNING" \| "ERROR" \| "CRITICAL" \| None "WARNING" Log level for suspicious requests. None disables.
log_request_level Same as above None Log level for all requests. None disables.
log_country_check_level Same as above (default "INFO") "INFO" Log level for non-block country verdicts (whitelisted / not-affected). None disables. Blocked-country hits log at log_suspicious_level instead (default WARNING); no-rules / no-geolocation always log at DEBUG.
log_format "text" \| "json" "text" Log output format.
custom_log_file str \| None None Path to a custom log file.

Agent / Telemetry

Internal Configuration

Agent fields are typically not exposed to end users. They are used for Guard Agent SaaS integration.

Field Type Default Description
enable_agent bool False Enable Guard Agent telemetry.
agent_api_key str \| None None API key for the SaaS platform.
agent_strict bool False Raise at middleware init instead of degrading to agent-off when an enabled agent cannot be initialized.
agent_endpoint str "https://api.guard-core.com" Agent endpoint URL.
agent_project_id str \| None None Project identifier.
agent_buffer_size int 100 Events to buffer before flush.
agent_flush_interval int 30 Seconds between automatic flushes.
agent_enable_events bool True Send security events.
agent_enable_metrics bool True Send performance metrics.
agent_timeout int 30 HTTP request timeout in seconds.
agent_retry_attempts int 3 Retry attempts for failed requests.
agent_project_encryption_key str \| None None Per-project AES-256-GCM key that switches the agent to the encrypted events endpoint. Required for API keys with encryption enforced server-side.
agent_guard_version str \| None None Framework wrapper version (e.g. fastapi-guard's __version__) reported alongside agent telemetry.
agent_status_interval int 300 Seconds between agent status reports to the SaaS. Must be between 60 and 86400.
agent_high_watermark_ratio float \| None None Buffer occupancy ratio that triggers an early flush. None defers to the agent's own default (0.8).
agent_max_concurrent_flushes int \| None None Maximum concurrent early-flush operations. None defers to the agent's own default (1).
agent_buffer_overflow_policy Literal["drop", "block", "raise"] \| None None Behavior when the agent's in-memory buffer is full. None defers to the agent's own default ("drop").
agent_backoff_factor float \| None None Backoff factor for agent HTTP retries. None defers to the agent's own default.
agent_sensitive_headers list[str] \| None None Header names excluded from telemetry payloads. None defers to the agent's own default.
agent_max_payload_size int \| None None Maximum payload size in bytes included in events. None defers to the agent's own default.
agent_compression_enabled bool \| None None Gzip-compress outgoing batch bodies above agent_compression_threshold. None defers to the agent's own default.
agent_compression_threshold int \| None None Minimum body size in bytes before gzip compression applies. None defers to the agent's own default.
agent_install_id str \| None None Override the agent install ID. None auto-generates one.
agent_payload_signing_secret str \| None None HMAC-SHA256 secret used to sign the X-Payload-Signature header.

Validator: agent_api_key is required when enable_agent is True. agent_buffer_overflow_policy rejects any value other than "drop", "block", or "raise" at construction time.

SecurityConfig.on_error (documented under Core Settings hooks) is also forwarded to AgentConfig.on_error when the agent is enabled, so the same callback receives agent-side transport_send and encryption failures in addition to guard-core's own agent_init and geoip failures.

All eleven fields above with a None default follow the same rule: to_agent_config() omits a field from the AgentConfig call entirely when it is None, so an unset field is controlled by AgentConfig's own default rather than by a value duplicated into guard-core.


Dynamic Rules

Field Type Default Description
enable_dynamic_rules bool False Enable dynamic rule updates from SaaS platform.
dynamic_rule_interval int 300 Seconds between rule update checks.
emergency_mode bool False Emergency lockdown mode (set by dynamic rules).
emergency_whitelist list[str] [] Emergency whitelist IPs (set by dynamic rules).

Validator: enable_agent must be True when enable_dynamic_rules is True.


Validators

SecurityConfig includes Pydantic validators that run on instantiation:

Validator Fields Behavior
validate_ip_lists whitelist, blacklist Validates IP addresses and CIDR ranges. Raises ValueError on invalid entries.
validate_trusted_proxies trusted_proxies Validates proxy IPs and CIDR ranges. Raises ValueError on invalid entries.
validate_proxy_depth trusted_proxy_depth Must be >= 1. Raises ValueError otherwise.
validate_cloud_providers block_cloud_providers Silently filters invalid providers: an entry is kept only if the part before an optional :!region suffix is "AWS", "GCP", or "Azure".
validate_geo_ip_handler_exists model-level Requires geo_ip_handler when blocked_countries or whitelist_countries is set. Falls back to IPInfoManager if ipinfo_token is provided.
validate_agent_config model-level Requires agent_api_key when enable_agent is True. Requires enable_agent when enable_dynamic_rules is True.
validate_optional_extras_installed model-level Requires the redis extra when enable_redis is True, the cloud extra (aiohttp or requests) when cloud blocking is enabled (block_cloud_providers or enable_dynamic_rules), and the geo extra (maxminddb) when country rules are configured with no custom geo_ip_handler. Raises ValueError naming the missing extra's install command, checked via importlib.util.find_spec (never a bare import). See Installation.
warn_unknown_fields model-level, mode="before" Compares the constructor keyword arguments against model_fields (and any field's alias) and logs a guard_core.models warning naming each unknown key, since SecurityConfig still allows unknown keys through (extra="ignore") rather than raising. Construction still succeeds and the unknown key is still dropped; only a log line is added, so a typo'd field name is no longer a silent no-op. extra="forbid" is the intended behavior at a future major release.

Silent filtering

validate_cloud_providers silently drops unrecognized provider names. {"AWS", "InvalidProvider"} becomes {"AWS"} without raising an error.