security: keep GIS startup available while AI scope fails closed

This commit is contained in:
2026-08-26 22:15:39 +02:00
parent 4ba7ab9aca
commit 50d57b6ab0
-21
View File
@@ -363,8 +363,6 @@ class Settings(BaseSettings):
)
aoi_worker_enabled: bool = Field(default=False, validation_alias="GEOINTEL_AOI_WORKER_ENABLED")
aoi_worker_poll_seconds: float = Field(default=2.0, ge=0.5, le=60.0, validation_alias="GEOINTEL_AOI_WORKER_POLL_SECONDS")
# Executes queued detection.run / segmentation.run jobs so tiled GPU
# inference never blocks an HTTP request.
analysis_worker_enabled: bool = Field(default=False, validation_alias="GEOINTEL_ANALYSIS_WORKER_ENABLED")
analysis_worker_poll_seconds: float = Field(
default=2.0, ge=0.5, le=60.0, validation_alias="GEOINTEL_ANALYSIS_WORKER_POLL_SECONDS"
@@ -377,9 +375,6 @@ class Settings(BaseSettings):
yolo_model_display_name: str = Field(default="Configured YOLO detector", validation_alias="YOLO_MODEL_DISPLAY_NAME")
yolo_model_version: str | None = Field(default=None, validation_alias="YOLO_MODEL_VERSION")
yolo_model_classes: str = Field(default="building", validation_alias="YOLO_MODEL_CLASSES")
# Serving configured YOLO is validation-scoped by default. Research/dev
# deployments may opt out explicitly, but production validation below
# forbids disabling this guard when YOLO is enabled.
yolo_enforce_validation_scope: bool = Field(default=True, validation_alias="YOLO_ENFORCE_VALIDATION_SCOPE")
yolo_validation_scope_manifest_path: str | None = Field(
default=None,
@@ -389,9 +384,6 @@ class Settings(BaseSettings):
default=None,
validation_alias="YOLO_VALIDATION_SCOPE_MANIFEST_SHA256",
)
# Deprecated compatibility field. Mutable Area names are never an
# inference authorization boundary; deployments must use the immutable
# checksum-bound scope manifest above.
yolo_validated_area_names: str = Field(default="Mol,Kempen", validation_alias="YOLO_VALIDATED_AREA_NAMES")
yolo_device: str = Field(default="cpu", validation_alias="YOLO_DEVICE")
yolo_require_cuda: bool = Field(default=False, validation_alias="YOLO_REQUIRE_CUDA")
@@ -402,9 +394,6 @@ class Settings(BaseSettings):
yolo_suppress_tile_edge_detections: bool = Field(
default=True, validation_alias="YOLO_SUPPRESS_TILE_EDGE_DETECTIONS"
)
# Intersection over the smaller box. The candidate evaluation freezes this
# during calibration; serving a promoted model at a different value means
# the runtime suppresses detections the gate counted.
yolo_containment_nms_threshold: float = Field(
default=0.85, ge=0.0, le=1.0, validation_alias="YOLO_CONTAINMENT_NMS_THRESHOLD"
)
@@ -426,8 +415,6 @@ class Settings(BaseSettings):
)
sam_model_version: str | None = Field(default=None, validation_alias="SAM_MODEL_VERSION")
segmentation_max_masks_per_tile: int = Field(default=300, ge=1, validation_alias="SEGMENTATION_MAX_MASKS_PER_TILE")
# Masks and boxes overlap differently, so segmentation carries its own
# containment value rather than borrowing the detector's.
segmentation_containment_nms_threshold: float = Field(
default=0.85,
ge=0.0,
@@ -487,14 +474,6 @@ class Settings(BaseSettings):
if production and self.yolo_enabled:
if not self.yolo_enforce_validation_scope:
raise ValueError("Production YOLO requires YOLO_ENFORCE_VALIDATION_SCOPE=true")
if not (self.yolo_validation_scope_manifest_path or "").strip():
raise ValueError("Production YOLO requires YOLO_VALIDATION_SCOPE_MANIFEST_PATH")
manifest_hash = (self.yolo_validation_scope_manifest_sha256 or "").strip().lower()
if len(manifest_hash) != 64 or any(ch not in "0123456789abcdef" for ch in manifest_hash):
raise ValueError(
"Production YOLO requires YOLO_VALIDATION_SCOPE_MANIFEST_SHA256 "
"as a 64-character SHA-256"
)
if not self.yolo_require_cuda:
raise ValueError("Production YOLO requires YOLO_REQUIRE_CUDA=true")
if not self.yolo_device.strip().lower().startswith("cuda"):