Files
ai-contract-platform/yuqei-infra/observability/receive-production-alert.py
T
yuqei ce7e040e98
Build and Test Quality Gate / Contract Web typecheck and build (pull_request) Successful in 7m28s
Build and Test Quality Gate / Python tests and Contract API migration (pull_request) Failing after 26m12s
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Successful in 6m33s
feat: close production trust and operations gates
2026-08-12 12:58:10 +08:00

208 lines
6.8 KiB
Python

#!/usr/bin/env python3
"""Receive authenticated production health alerts and persist immutable receipts."""
from __future__ import annotations
import argparse
import hashlib
import hmac
import json
import os
import re
import sys
import uuid
from datetime import datetime, timezone
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any
from urllib.parse import unquote, urlparse
TOKEN_PATTERN = re.compile(r"^[A-Za-z0-9_-]{43,128}$")
DEFAULT_MAX_BODY_BYTES = 1024 * 1024
def read_token(path: Path) -> str:
token = path.read_text(encoding="utf-8").strip()
if not TOKEN_PATTERN.fullmatch(token):
raise ValueError("alert receiver token must be 43-128 URL-safe characters")
return token
def parse_alert_body(raw: bytes) -> dict[str, Any]:
try:
payload = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise ValueError("alert body must be valid UTF-8 JSON") from exc
if not isinstance(payload, dict):
raise ValueError("alert body must be a JSON object")
if payload.get("status") not in {"warning", "fail"}:
raise ValueError("alert status must be warning or fail")
if not isinstance(payload.get("checked_at"), str):
raise ValueError("alert checked_at is required")
return payload
def persist_event(
event_dir: Path,
*,
payload: dict[str, Any],
raw: bytes,
source: str,
) -> dict[str, str]:
received_at = datetime.now(timezone.utc).isoformat()
event_id = f"yuqei-alert-{uuid.uuid4().hex}"
receipt = {
"schema_version": "yuqei-production-alert-receipt.v1",
"event_id": event_id,
"received_at": received_at,
"source": source,
"body_sha256": hashlib.sha256(raw).hexdigest(),
"payload": payload,
}
event_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
target = event_dir / f"{event_id}.json"
encoded = (json.dumps(receipt, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
descriptor = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
with os.fdopen(descriptor, "wb") as stream:
stream.write(encoded)
stream.flush()
os.fsync(stream.fileno())
except Exception:
target.unlink(missing_ok=True)
raise
return {
"event_id": event_id,
"received_at": received_at,
"body_sha256": receipt["body_sha256"],
}
class AlertReceiver(ThreadingHTTPServer):
daemon_threads = True
def __init__(
self,
server_address: tuple[str, int],
*,
token: str,
event_dir: Path,
max_body_bytes: int,
) -> None:
super().__init__(server_address, AlertHandler)
self.expected_path = f"/ops/alerts/{token}"
self.event_dir = event_dir
self.max_body_bytes = max_body_bytes
class AlertHandler(BaseHTTPRequestHandler):
server: AlertReceiver
def do_POST(self) -> None: # noqa: N802
request_path = unquote(urlparse(self.path).path)
if not hmac.compare_digest(request_path, self.server.expected_path):
self._respond(HTTPStatus.NOT_FOUND, {"error": "not_found"})
return
content_type = self.headers.get_content_type()
if content_type != "application/json":
self._respond(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, {"error": "application_json_required"})
return
try:
content_length = int(self.headers.get("Content-Length", ""))
except ValueError:
content_length = -1
if not 0 < content_length <= self.server.max_body_bytes:
self._respond(HTTPStatus.REQUEST_ENTITY_TOO_LARGE, {"error": "invalid_content_length"})
return
raw = self.rfile.read(content_length)
if len(raw) != content_length:
self._respond(HTTPStatus.BAD_REQUEST, {"error": "incomplete_body"})
return
try:
payload = parse_alert_body(raw)
receipt = persist_event(
self.server.event_dir,
payload=payload,
raw=raw,
source=self.headers.get("X-Forwarded-For", self.client_address[0]),
)
except (OSError, ValueError):
self._respond(HTTPStatus.BAD_REQUEST, {"error": "invalid_alert"})
return
self._respond(HTTPStatus.CREATED, receipt)
def do_GET(self) -> None: # noqa: N802
self._respond(HTTPStatus.METHOD_NOT_ALLOWED, {"error": "method_not_allowed"})
def log_message(self, message: str, *args: object) -> None:
print(f"ALERT_RECEIVER {self.address_string()} {message % args}", file=sys.stderr)
def _respond(self, status: HTTPStatus, payload: dict[str, str]) -> None:
raw = json.dumps(payload, separators=(",", ":")).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(raw)))
self.send_header("Cache-Control", "no-store")
self.send_header("X-Content-Type-Options", "nosniff")
self.end_headers()
self.wfile.write(raw)
def build_server(
bind: str,
port: int,
*,
token_file: Path,
event_dir: Path,
max_body_bytes: int,
) -> AlertReceiver:
if not 0 <= port <= 65535:
raise ValueError("port must be between 0 and 65535")
if not 1024 <= max_body_bytes <= 4 * 1024 * 1024:
raise ValueError("maximum body size must be between 1 KiB and 4 MiB")
return AlertReceiver(
(bind, port),
token=read_token(token_file),
event_dir=event_dir.resolve(),
max_body_bytes=max_body_bytes,
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--bind", default="127.0.0.1")
parser.add_argument("--port", type=int, default=19091)
parser.add_argument("--token-file", type=Path, required=True)
parser.add_argument("--event-dir", type=Path, required=True)
parser.add_argument("--max-body-bytes", type=int, default=DEFAULT_MAX_BODY_BYTES)
return parser.parse_args()
def main() -> int:
args = parse_args()
try:
server = build_server(
args.bind,
args.port,
token_file=args.token_file,
event_dir=args.event_dir,
max_body_bytes=args.max_body_bytes,
)
except (OSError, ValueError) as exc:
print(f"ALERT_RECEIVER_FAIL error={type(exc).__name__}", file=sys.stderr)
return 2
print(f"ALERT_RECEIVER_READY bind={args.bind} port={args.port}", flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
finally:
server.server_close()
return 0
if __name__ == "__main__":
raise SystemExit(main())