Configuration
The adapter has exactly two options (WithMaxBodyBytes, WithLogger); all
security tuning is engine configuration. See the
guard-core-go configuration reference
for the full SecurityConfig surface.
Minimal tuned setup
cfg := guardcore.DefaultSecurityConfig()
cfg.EnableRateLimiting = true
cfg.RateLimit = 30
cfg.RateLimitWindow = 60
cfg.EndpointRateLimits = map[string]guardcore.RateLimitEntry{
"/rate/strict": {Requests: 1, Window: 10},
}
cfg.EnableIPBanning = true
cfg.AutoBanThreshold = 5
cfg.AutoBanDuration = 300
cfg.CustomErrorResponses = map[int]string{
403: "Blocked by echo-guard",
}
Address headers
The Python engine skips ssrf scanning for address headers (host,
x-forwarded-for, x-real-ip, ...) automatically. The Go engine does not
apply that built-in exclusion yet, and the Echo adapter passes an explicit
Host header, so mirror the exclusion when clients can send internal
hostnames:
cfg.ExcludedDetectionHeaders = map[string]bool{
"host": true, "origin": true, "via": true,
"x-forwarded-for": true, "x-forwarded-host": true,
"x-real-ip": true, "x-client-ip": true,
}
Redis
Distributed bans and rate limits require Redis:
cfg.EnableRedis = true
cfg.RedisURL = os.Getenv("REDIS_URL") // e.g. redis://redis:6379
cfg.RedisPrefix = "echo_guard:"
Without Redis the managers fall back to in-process state, which does not share across replicas.
Body inspection
WithMaxBodyBytes bounds what the detector sees. Bodies larger than the
limit are still forwarded to your handler; only the inspected prefix is
truncated. The default matches the engine's MaxBodyInspectBytes (262144).
Trusted proxies
When behind a reverse proxy, trust only the proxy hop so the engine resolves the real client IP from forwarded headers:
cfg.TrustedProxies = []string{"172.16.0.0/12", "10.0.0.0/8"}
cfg.TrustedProxyDepth = 1
Behavior rules
The engine's behavioral surface is configurable through the global fields:
cfg.GlobalBehaviorRules = []guardcore.BehaviorRuleConfig{
{RuleType: "usage", Threshold: 100, Window: 3600, Action: "ban"},
}
cfg.BehaviorScanResponseBody = true
cfg.BehaviorMaxResponseBodyInspectBytes = 262144
BehaviorScanResponseBody gates return-pattern rules that read the response
body (regex:, json:, or bare substring patterns; status: patterns work
without it). With the flag on, the adapter captures the leading
BehaviorMaxResponseBodyInspectBytes of every pass-through response body and
reports status code plus captured prefix to Engine.ProcessResponse after
your handler runs, mirroring the reference response factory's behavioral
phase. Return rules never modify the response. Note: a handler that returns
an Echo HTTPError is written by Echo's error handler after the middleware
returns, so the engine observes that response too late for return rules; have
the handler write the response itself when return-pattern rules matter.
Geo lifecycle
Country rules resolve through the engine's geo lifecycle. A token enables the
full IPInfo download/refresh lifecycle; a database path keeps local-file
mode. OnGeoEvent receives country_blocked, geo_lookup_failed, and
decorator_violation events:
cfg.IPInfoToken = os.Getenv("IPINFO_TOKEN")
cfg.IPInfoMaxAge = 86400 // 0 falls back to the reference default
cfg.BlockedCountries = []string{"CN"}
cfg.OnGeoEvent = func(ev guardcore.GeoEvent) { ... }
Route-level country rules (RouteConfig.BlockedCountries /
WhitelistCountries) emit the same events through the same hook.
Per-route detection exclusions
Route configuration lives in the engine registry and reaches the middleware
through guardecho.WithRouteID (see Usage). Every
guardcore.RouteConfig field is reachable, including the per-route detection
exclusion surface:
engine.Routes.Register("search", func(rc *guardcore.RouteConfig) {
rc.ExcludedDetectionParams = map[string]bool{"q": true}
rc.ExcludedDetectionHeaders = map[string]bool{"referer": true}
rc.ExcludedDetectionBodyFields = map[string]bool{"note": true}
rc.EnabledDetectionCategories = []string{"xss", "sqli"}
scanOff := false
rc.DetectionScanBody = &scanOff
rc.BehaviorRules = []guardcore.BehaviorRuleConfig{
{RuleType: "usage", Threshold: 10, Window: 60, Action: "ban"},
}
})
CORS
Set cfg.EnableCORS = true (plus any CORSAllowOrigins, CORSAllowMethods,
CORSAllowHeaders, CORSAllowCredentials, CORSExposeHeaders,
CORSMaxAge tuning). Preflights are short-circuited by the engine through
guardecho.New, blocked responses carry the CORS headers over the
security-header set, and the adapter merges
Engine.CORSResponseHeaders(req) into every pass-through response.
Custom error bodies
cfg.CustomErrorResponses replaces the body of any engine verdict status
code; the status code and security headers stay engine-owned.