Migrating from v1.x to v2.0¶
guard-core 2.0.0 ships operator-facing security controls and a pluggable IP lifecycle. This page lists every breaking change and the matching migration step. See the v2.0.0 changelog entry for the full list of additions.
Pin guard-core¶
All four framework adapters (fastapi-guard, flaskapi-guard, djapi-guard, tornadoapi-guard) need a release pinning guard-core>=2.0.0. If you use one of those adapters, upgrade it in lockstep.
detect_penetration_attempt returns DetectionResult¶
Both detect_penetration_attempt() and detect_penetration_patterns() now return a DetectionResult dataclass instead of tuple[bool, str]. Tuple-unpacking callers must migrate.
detected, trigger = await detect_penetration_attempt(request)
result = await detect_penetration_attempt(request)
detected, trigger = result.is_threat, result.trigger_info
Read result.threat_categories and result.threat_scores for the new per-category metadata. The dataclass is forward-compatible — additional fields can be added without breaking call sites that read by attribute.
suspicious_request_counts is now nested¶
GuardMiddlewareProtocol.suspicious_request_counts changed shape from dict[str, int] to dict[str, dict[str, int]]. The outer key is the IP, the inner key is the detection category, the value is the per-category count.
self.suspicious_request_counts[ip] += 1
self.suspicious_request_counts.setdefault(ip, {})
counts = self.suspicious_request_counts[ip]
counts[category] = counts.get(category, 0) + 1
total = sum(self.suspicious_request_counts.get(ip, {}).values())
Every adapter middleware that read or wrote this attribute must migrate. The built-in SuspiciousActivityCheck already does this — only adapter-side reads need attention.
Compiled-pattern tuples are 3-tuples¶
SusPatternsManager compiled-pattern collections changed from 2-tuples to 3-tuples. The third element is the category label ("xss", "sqli", "custom", ...). Any direct iteration over compiled_patterns or get_all_compiled_patterns() must unpack three elements.
for pattern, contexts in await mgr.get_all_compiled_patterns():
...
for pattern, contexts, category in await mgr.get_all_compiled_patterns():
...
Custom user patterns added via add_pattern(..., custom=True) carry the literal label "custom" and run regardless of enabled_categories filtering.
_check_value_enhanced / _check_request_component return 3-tuples¶
Internal helpers in guard_core.utils now return tuple[bool, str, list[dict]] instead of tuple[bool, str]. None of the framework adapters reach into these helpers directly; the change is flagged in case downstream code does.
Cloud-IP cache Redis namespace migration¶
The Redis cache for cloud-provider IP ranges moved from cloud_ranges (CSV per provider) to guard:cloud_ip (JSON-encoded sorted list per provider). The legacy CSV path is still reachable when CloudManager._store is None, but the default and the new RedisCloudIpStore write to the new namespace.
If you have ops tooling, dashboards, or sidecars reading those keys directly, switch to the new namespace. See Cloud IP Store for details and the protocol contract.
Additive (no migration required)¶
These additions ship in v2.0.0 but do not require any migration:
excluded_detection_headers,excluded_detection_params,excluded_detection_body_fieldsdefault to empty sets — existing detection coverage is unchanged.enabled_detection_categoriesdefaults to the fullALL_DETECTION_CATEGORIESset.threat_ban_configdefaults to{}and falls back to the existing flatauto_ban_threshold/auto_ban_durationpolicy.global_behavior_rulesdefaults to[].lazy_initdefaults toFalse(eager bootstrap unchanged). When set toTrue, the IPInfo MMDB download and cloud-IP fetches run as a background task during startup; the first request is no longer blocked while they complete (changed in v2.1.0).geo_ip_db_max_agedefaults to86400(matches the old hardcoded value).cloud_ip_store=Noneuses the in-memory store, auto-upgraded to Redis when Redis is enabled.
Adopt these incrementally as needed.