1022 lines
38 KiB
Python
1022 lines
38 KiB
Python
#!/usr/bin/env python3
|
|
"""Fail-closed validator for the complete G7 external evidence gate.
|
|
|
|
All four artifacts are required: authenticated public physical-microphone soak,
|
|
process-local voice runtime sampling, pinned deployment topology sampling, and
|
|
an independent human-held-out voice-gain pack. No artifact may contain raw audio
|
|
or transcripts, and synthetic evidence cannot satisfy this gate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import math
|
|
import ntpath
|
|
import sys
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
API_ROOT = REPO_ROOT / "apps/api"
|
|
if str(API_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(API_ROOT))
|
|
|
|
from app.contracts.g7_external_evidence import G7HumanVoiceGainEvidencePack # noqa: E402
|
|
from app.services.g7_voice_gain_evidence import evaluate_human_voice_gain # noqa: E402
|
|
|
|
|
|
MINIMUM_SOAK_SECONDS = 3_000.0
|
|
PINNED_PSUTIL_VERSION = "6.1.1"
|
|
|
|
# 이 게이트는 특정 벤더가 아니라 **운영하기로 결정한 provider** 를 강제한다.
|
|
# 2026-08-08 소유자 결정: STT 는 노트북 상주 faster-whisper(`local_whisper`),
|
|
# TTS 는 노트북 MeloTTS(`melotts`). 둘 다 MIT 라 운영 사용 제약이 없다.
|
|
# 이전 값은 벤더 하나(`deepgram`/`openai`)가 하드코딩돼 있었을 뿐 결정 기록이 아니었다.
|
|
# 근거는 docs/decisions/local-voice-stack.md.
|
|
#
|
|
# 목록은 닫혀 있다. 배치 STT(`openai`)는 interim/final 계약을 만족할 수 없어
|
|
# 여기 들어오지 못한다. 어떤 경우에도 `expected_* == ready_*` 결속은 유지되므로
|
|
# 선언한 provider 와 실제로 돈 provider 가 다르면 계속 실패한다.
|
|
ALLOWED_STT_PROVIDERS = ("local_whisper", "deepgram")
|
|
ALLOWED_TTS_PROVIDERS = ("melotts", "higgs", "openai")
|
|
|
|
|
|
def load_json(path: Path, errors: list[str], label: str) -> dict[str, Any]:
|
|
try:
|
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
except FileNotFoundError:
|
|
errors.append(f"{label}:missing")
|
|
return {}
|
|
except (OSError, UnicodeError, json.JSONDecodeError):
|
|
errors.append(f"{label}:unreadable")
|
|
return {}
|
|
if not isinstance(payload, dict):
|
|
errors.append(f"{label}:invalid_shape")
|
|
return {}
|
|
return payload
|
|
|
|
|
|
def _number(value: object) -> float | None:
|
|
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
return None
|
|
result = float(value)
|
|
return result if math.isfinite(result) else None
|
|
|
|
|
|
def _require(errors: list[str], condition: bool, code: str) -> None:
|
|
if not condition:
|
|
errors.append(code)
|
|
|
|
|
|
def _iso(value: object) -> datetime | None:
|
|
if not isinstance(value, str) or not value:
|
|
return None
|
|
try:
|
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
except ValueError:
|
|
return None
|
|
if parsed.tzinfo is None:
|
|
parsed = parsed.replace(tzinfo=UTC)
|
|
return parsed.astimezone(UTC)
|
|
|
|
|
|
def _lower_hex(value: object, lengths: set[int]) -> bool:
|
|
return (
|
|
isinstance(value, str)
|
|
and len(value) in lengths
|
|
and value == value.lower()
|
|
and all(character in "0123456789abcdef" for character in value)
|
|
)
|
|
|
|
|
|
def validate_public_soak(payload: dict[str, Any], errors: list[str]) -> None:
|
|
prefix = "voice_soak"
|
|
_require(
|
|
errors,
|
|
payload.get("schema_version") == "vignette.g7-public-voice-soak.v4",
|
|
f"{prefix}:schema",
|
|
)
|
|
_require(errors, payload.get("mode") == "public_soak", f"{prefix}:mode")
|
|
_require(errors, payload.get("status") == "passed", f"{prefix}:status")
|
|
requested = _number(payload.get("requested_duration_seconds"))
|
|
elapsed = _number(payload.get("elapsed_seconds"))
|
|
_require(
|
|
errors,
|
|
requested is not None and requested >= MINIMUM_SOAK_SECONDS,
|
|
f"{prefix}:duration_requested",
|
|
)
|
|
_require(
|
|
errors,
|
|
elapsed is not None and requested is not None and elapsed >= requested,
|
|
f"{prefix}:duration_elapsed",
|
|
)
|
|
for field in (
|
|
"microphone_device_enumerated",
|
|
"physical_capture_confirmed",
|
|
"physical_microphone_used",
|
|
"public_wss_used",
|
|
"authenticated_public_wss_ready",
|
|
"real_database_session_binding_required",
|
|
"ready_provider_metadata_validated",
|
|
"cookie_present",
|
|
"session_id_present",
|
|
"cloudflare_ray_present",
|
|
"unauthenticated_handshake_accepted",
|
|
):
|
|
_require(errors, payload.get(field) is True, f"{prefix}:{field}")
|
|
for field in (
|
|
"raw_audio_retained",
|
|
"provider_overrides_used",
|
|
"cookie_value_logged",
|
|
):
|
|
_require(errors, payload.get(field) is False, f"{prefix}:{field}")
|
|
_require(errors, payload.get("tls_version") == "TLSv1.3", f"{prefix}:tls")
|
|
_require(
|
|
errors,
|
|
payload.get("unauthenticated_close_code") == 1008,
|
|
f"{prefix}:unauth_close",
|
|
)
|
|
_require(errors, payload.get("close_code") == 1000, f"{prefix}:auth_close")
|
|
_require(
|
|
errors,
|
|
payload.get("expected_stt_provider") in ALLOWED_STT_PROVIDERS,
|
|
f"{prefix}:stt_provider_not_operated",
|
|
)
|
|
_require(
|
|
errors,
|
|
payload.get("expected_tts_provider") in ALLOWED_TTS_PROVIDERS,
|
|
f"{prefix}:tts_provider_not_operated",
|
|
)
|
|
for expected, ready in (
|
|
("expected_stt_provider", "ready_stt_provider"),
|
|
("expected_stt_model", "ready_stt_model"),
|
|
("expected_tts_provider", "ready_tts_provider"),
|
|
("expected_tts_model", "ready_tts_model"),
|
|
):
|
|
_require(
|
|
errors,
|
|
bool(payload.get(expected)) and payload.get(expected) == payload.get(ready),
|
|
f"{prefix}:{ready}_mismatch",
|
|
)
|
|
|
|
attempted = payload.get("turns_attempted")
|
|
succeeded = payload.get("turns_succeeded")
|
|
metrics = payload.get("turn_transcript_metrics")
|
|
_require(
|
|
errors,
|
|
isinstance(attempted, int) and attempted >= 10,
|
|
f"{prefix}:turns_attempted",
|
|
)
|
|
_require(errors, succeeded == attempted, f"{prefix}:turns_succeeded")
|
|
_require(
|
|
errors,
|
|
isinstance(metrics, list) and len(metrics) == succeeded,
|
|
f"{prefix}:turn_metrics_count",
|
|
)
|
|
if isinstance(metrics, list):
|
|
interim_total = 0
|
|
final_total = 0
|
|
seen_turns: set[int] = set()
|
|
for item in metrics:
|
|
if not isinstance(item, dict):
|
|
errors.append(f"{prefix}:turn_metric_shape")
|
|
continue
|
|
turn = item.get("turn_number")
|
|
interim = item.get("interim_transcript_frames")
|
|
final = item.get("speech_final_transcript_frames")
|
|
_require(
|
|
errors,
|
|
isinstance(turn, int) and turn > 0 and turn not in seen_turns,
|
|
f"{prefix}:turn_number",
|
|
)
|
|
if isinstance(turn, int):
|
|
seen_turns.add(turn)
|
|
_require(
|
|
errors,
|
|
isinstance(interim, int) and interim >= 1,
|
|
f"{prefix}:interim_missing",
|
|
)
|
|
_require(errors, final == 1, f"{prefix}:speech_final_not_exact")
|
|
_require(
|
|
errors,
|
|
_number(item.get("first_interim_latency_ms")) is not None,
|
|
f"{prefix}:interim_latency",
|
|
)
|
|
_require(
|
|
errors,
|
|
_number(item.get("speech_final_latency_ms")) is not None,
|
|
f"{prefix}:final_latency",
|
|
)
|
|
interim_total += interim if isinstance(interim, int) else 0
|
|
final_total += final if isinstance(final, int) else 0
|
|
_require(
|
|
errors,
|
|
payload.get("interim_transcript_frames") == interim_total,
|
|
f"{prefix}:interim_total",
|
|
)
|
|
_require(
|
|
errors,
|
|
payload.get("speech_final_transcript_frames") == final_total,
|
|
f"{prefix}:final_total",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(succeeded, int) and payload.get("reply_frames", 0) >= succeeded,
|
|
f"{prefix}:reply_frames",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(succeeded, int) and payload.get("tts_end_frames", 0) >= succeeded,
|
|
f"{prefix}:tts_end_frames",
|
|
)
|
|
_require(errors, payload.get("tts_binary_bytes", 0) > 0, f"{prefix}:tts_audio")
|
|
_require(errors, payload.get("captured_pcm_bytes", 0) > 0, f"{prefix}:captured_pcm")
|
|
_require(errors, not payload.get("blockers"), f"{prefix}:blockers")
|
|
_require(errors, payload.get("failure_type") is None, f"{prefix}:failure")
|
|
|
|
|
|
def validate_runtime(payload: dict[str, Any], errors: list[str]) -> None:
|
|
prefix = "runtime"
|
|
_require(
|
|
errors,
|
|
payload.get("schema_version") == "vignette.g7-runtime-sampling.v1",
|
|
f"{prefix}:schema",
|
|
)
|
|
_require(errors, payload.get("status") == "passed", f"{prefix}:status")
|
|
_require(
|
|
errors,
|
|
payload.get("privacy_boundary")
|
|
== "metadata_only_no_audio_transcript_session_or_cookie_values",
|
|
f"{prefix}:privacy",
|
|
)
|
|
_require(
|
|
errors,
|
|
payload.get("cookie_present") is True
|
|
and payload.get("cookie_value_logged") is False,
|
|
f"{prefix}:cookie_boundary",
|
|
)
|
|
requested = payload.get("requested_samples")
|
|
completed = payload.get("samples_completed")
|
|
interval = _number(payload.get("interval_seconds"))
|
|
_require(
|
|
errors,
|
|
isinstance(requested, int) and requested == completed and requested > 1,
|
|
f"{prefix}:sample_count",
|
|
)
|
|
_require(
|
|
errors,
|
|
interval is not None
|
|
and isinstance(completed, int)
|
|
and (completed - 1) * interval >= MINIMUM_SOAK_SECONDS,
|
|
f"{prefix}:coverage",
|
|
)
|
|
samples = payload.get("samples")
|
|
_require(
|
|
errors,
|
|
isinstance(samples, list) and len(samples) == completed,
|
|
f"{prefix}:samples",
|
|
)
|
|
high = payload.get("high_water")
|
|
_require(errors, isinstance(high, dict), f"{prefix}:high_water")
|
|
if isinstance(high, dict):
|
|
for field in (
|
|
"process_peak_rss_bytes",
|
|
"process_threads_max",
|
|
"websocket_high_water",
|
|
"streaming_provider_session_high_water",
|
|
"route_audio_buffer_high_water_bytes",
|
|
"streaming_event_queue_high_water_items",
|
|
):
|
|
_require(errors, (_number(high.get(field)) or 0) > 0, f"{prefix}:{field}")
|
|
capture_started = _iso(payload.get("started_at_utc"))
|
|
worker_started = _iso(payload.get("worker_started_at_utc"))
|
|
_require(
|
|
errors,
|
|
capture_started is not None
|
|
and worker_started is not None
|
|
and 0 <= (capture_started - worker_started).total_seconds() <= 120,
|
|
f"{prefix}:fresh_worker_required",
|
|
)
|
|
if isinstance(samples, list) and samples:
|
|
final = samples[-1].get("snapshot") if isinstance(samples[-1], dict) else None
|
|
counters = final.get("counters") if isinstance(final, dict) else None
|
|
limits = final.get("limits") if isinstance(final, dict) else None
|
|
_require(
|
|
errors,
|
|
isinstance(final, dict)
|
|
and final.get("schema_version") == "vignette.voice-runtime.v1",
|
|
f"{prefix}:snapshot_schema",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(final, dict) and final.get("scope") == "single_api_worker",
|
|
f"{prefix}:snapshot_scope",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(final, dict)
|
|
and final.get("privacy_boundary")
|
|
== "metadata_only_no_audio_transcript_or_session_ids",
|
|
f"{prefix}:snapshot_privacy",
|
|
)
|
|
_require(errors, isinstance(counters, dict), f"{prefix}:final_counters")
|
|
_require(
|
|
errors,
|
|
isinstance(limits, dict) and limits.get("uvicorn_ws_max_queue") == 4,
|
|
f"{prefix}:uvicorn_queue_cap",
|
|
)
|
|
if isinstance(counters, dict):
|
|
for field in (
|
|
"provider_fallback_total",
|
|
"websocket_error_total",
|
|
"audio_overflow_rejections_total",
|
|
):
|
|
_require(errors, counters.get(field) == 0, f"{prefix}:{field}")
|
|
|
|
|
|
def _validate_linux_compose_topology(
|
|
payload: dict[str, Any],
|
|
errors: list[str],
|
|
) -> None:
|
|
prefix = "topology"
|
|
targets = payload.get("targets")
|
|
_require(
|
|
errors,
|
|
isinstance(targets, dict) and set(targets) == {"api", "caddy"},
|
|
f"{prefix}:targets",
|
|
)
|
|
if isinstance(targets, dict):
|
|
capture_started = _iso(payload.get("started_at_utc"))
|
|
for role in ("api", "caddy"):
|
|
target = targets.get(role)
|
|
_require(errors, isinstance(target, dict), f"{prefix}:{role}_target")
|
|
if isinstance(target, dict):
|
|
_require(
|
|
errors,
|
|
isinstance(target.get("container_id"), str)
|
|
and len(target["container_id"]) == 64,
|
|
f"{prefix}:{role}_container",
|
|
)
|
|
digest = target.get("image_digest")
|
|
_require(
|
|
errors,
|
|
isinstance(digest, str)
|
|
and digest.startswith("sha256:")
|
|
and len(digest) == 71,
|
|
f"{prefix}:{role}_image",
|
|
)
|
|
target_started = _iso(target.get("started_at"))
|
|
_require(
|
|
errors,
|
|
capture_started is not None
|
|
and target_started is not None
|
|
and 0 <= (capture_started - target_started).total_seconds() <= 120,
|
|
f"{prefix}:{role}_fresh_container_required",
|
|
)
|
|
summary = payload.get("summary")
|
|
_require(errors, isinstance(summary, dict), f"{prefix}:summary")
|
|
if isinstance(summary, dict):
|
|
containers = summary.get("containers")
|
|
host_tcp = summary.get("host_tcp")
|
|
_require(errors, isinstance(containers, dict), f"{prefix}:container_summary")
|
|
if isinstance(containers, dict):
|
|
for role in ("api", "caddy"):
|
|
values = containers.get(role)
|
|
_require(errors, isinstance(values, dict), f"{prefix}:{role}_summary")
|
|
if isinstance(values, dict):
|
|
for field in (
|
|
"cgroup_memory_peak_bytes_max",
|
|
"cgroup_cpu_usage_usec_max",
|
|
"cgroup_pids_current_max",
|
|
"proc_vm_hwm_bytes_max",
|
|
"proc_threads_max",
|
|
"proc_fd_count_max",
|
|
):
|
|
_require(
|
|
errors,
|
|
(_number(values.get(field)) or 0) > 0,
|
|
f"{prefix}:{role}_{field}",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(host_tcp, dict)
|
|
and (_number(host_tcp.get("connections_max")) or 0) > 0,
|
|
f"{prefix}:host_tcp",
|
|
)
|
|
|
|
|
|
def _validate_windows_topology(
|
|
payload: dict[str, Any],
|
|
errors: list[str],
|
|
) -> None:
|
|
prefix = "topology"
|
|
requested = payload.get("requested")
|
|
_require(errors, isinstance(requested, dict), f"{prefix}:windows_requested")
|
|
if not isinstance(requested, dict):
|
|
return
|
|
expected_git_sha = requested.get("git_sha")
|
|
_require(
|
|
errors,
|
|
_lower_hex(expected_git_sha, {40, 64}),
|
|
f"{prefix}:windows_git_sha",
|
|
)
|
|
source_pin = requested.get("source_pin")
|
|
_require(
|
|
errors,
|
|
isinstance(source_pin, dict),
|
|
f"{prefix}:windows_source_pin",
|
|
)
|
|
expected_tree_sha = (
|
|
source_pin.get("git_tree_sha") if isinstance(source_pin, dict) else None
|
|
)
|
|
expected_scripts = (
|
|
source_pin.get("script_sha256") if isinstance(source_pin, dict) else None
|
|
)
|
|
expected_dependencies = (
|
|
source_pin.get("runtime_dependencies") if isinstance(source_pin, dict) else None
|
|
)
|
|
_require(
|
|
errors,
|
|
_lower_hex(expected_tree_sha, {40, 64}),
|
|
f"{prefix}:windows_git_tree_sha_pin",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(expected_scripts, dict)
|
|
and set(expected_scripts) == {"runner", "collector", "checker"}
|
|
and all(_lower_hex(value, {64}) for value in expected_scripts.values()),
|
|
f"{prefix}:windows_script_sha256_pin",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(expected_dependencies, dict)
|
|
and expected_dependencies == {"psutil": PINNED_PSUTIL_VERSION},
|
|
f"{prefix}:windows_psutil_version_pin",
|
|
)
|
|
|
|
source_provenance = payload.get("source_provenance")
|
|
_require(
|
|
errors,
|
|
isinstance(source_provenance, dict),
|
|
f"{prefix}:windows_source_provenance",
|
|
)
|
|
if isinstance(source_provenance, dict):
|
|
observed_scripts = source_provenance.get("script_sha256")
|
|
observed_dependencies = source_provenance.get("runtime_dependencies")
|
|
_require(
|
|
errors,
|
|
source_provenance.get("detached_head") is True,
|
|
f"{prefix}:windows_detached_head",
|
|
)
|
|
_require(
|
|
errors,
|
|
source_provenance.get("tracked_clean") is True,
|
|
f"{prefix}:windows_tracked_clean",
|
|
)
|
|
_require(
|
|
errors,
|
|
source_provenance.get("git_sha") == expected_git_sha,
|
|
f"{prefix}:windows_source_git_sha",
|
|
)
|
|
_require(
|
|
errors,
|
|
source_provenance.get("git_tree_sha") == expected_tree_sha,
|
|
f"{prefix}:windows_source_git_tree_sha",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(observed_scripts, dict)
|
|
and isinstance(expected_scripts, dict)
|
|
and observed_scripts == expected_scripts,
|
|
f"{prefix}:windows_source_script_sha256",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(observed_dependencies, dict)
|
|
and isinstance(expected_dependencies, dict)
|
|
and observed_dependencies == expected_dependencies,
|
|
f"{prefix}:windows_source_psutil_version",
|
|
)
|
|
repo_root = requested.get("repo_root")
|
|
_require(
|
|
errors,
|
|
isinstance(repo_root, str) and ntpath.isabs(repo_root),
|
|
f"{prefix}:windows_repo_root",
|
|
)
|
|
api_listen_port = requested.get("api_listen_port")
|
|
_require(
|
|
errors,
|
|
isinstance(api_listen_port, int)
|
|
and not isinstance(api_listen_port, bool)
|
|
and 1 <= api_listen_port <= 65_535,
|
|
f"{prefix}:windows_api_listen_port",
|
|
)
|
|
requested_roles = requested.get("roles")
|
|
_require(
|
|
errors,
|
|
isinstance(requested_roles, dict)
|
|
and set(requested_roles) == {"api", "cloudflared"},
|
|
f"{prefix}:windows_requested_roles",
|
|
)
|
|
targets = payload.get("targets")
|
|
_require(
|
|
errors,
|
|
isinstance(targets, dict) and set(targets) == {"api", "cloudflared"},
|
|
f"{prefix}:targets",
|
|
)
|
|
capture_started = _iso(payload.get("started_at_utc"))
|
|
target_pids: list[int] = []
|
|
if isinstance(targets, dict) and isinstance(requested_roles, dict):
|
|
for role in ("api", "cloudflared"):
|
|
target = targets.get(role)
|
|
expectation = requested_roles.get(role)
|
|
_require(errors, isinstance(target, dict), f"{prefix}:{role}_target")
|
|
_require(
|
|
errors,
|
|
isinstance(expectation, dict),
|
|
f"{prefix}:{role}_expectation",
|
|
)
|
|
if not isinstance(target, dict) or not isinstance(expectation, dict):
|
|
continue
|
|
_require(
|
|
errors,
|
|
target.get("role") == role,
|
|
f"{prefix}:{role}_role",
|
|
)
|
|
pid = target.get("pid")
|
|
_require(
|
|
errors,
|
|
isinstance(pid, int) and not isinstance(pid, bool) and pid > 0,
|
|
f"{prefix}:{role}_pid",
|
|
)
|
|
if isinstance(pid, int) and not isinstance(pid, bool):
|
|
target_pids.append(pid)
|
|
_require(
|
|
errors,
|
|
pid == expectation.get("pid"),
|
|
f"{prefix}:{role}_pid_pin",
|
|
)
|
|
executable_sha256 = target.get("executable_sha256")
|
|
_require(
|
|
errors,
|
|
_lower_hex(executable_sha256, {64})
|
|
and executable_sha256 == expectation.get("expected_executable_sha256"),
|
|
f"{prefix}:{role}_executable_sha256",
|
|
)
|
|
_require(
|
|
errors,
|
|
_lower_hex(target.get("command_line_sha256"), {64}),
|
|
f"{prefix}:{role}_command_line_sha256",
|
|
)
|
|
executable_name = target.get("executable_name")
|
|
expected_name = expectation.get("expected_executable_name")
|
|
_require(
|
|
errors,
|
|
isinstance(executable_name, str)
|
|
and isinstance(expected_name, str)
|
|
and executable_name.casefold() == expected_name.casefold(),
|
|
f"{prefix}:{role}_executable_name",
|
|
)
|
|
cwd = target.get("cwd")
|
|
expected_cwd = expectation.get("expected_cwd")
|
|
_require(
|
|
errors,
|
|
isinstance(cwd, str)
|
|
and isinstance(expected_cwd, str)
|
|
and ntpath.normcase(ntpath.normpath(cwd))
|
|
== ntpath.normcase(ntpath.normpath(expected_cwd)),
|
|
f"{prefix}:{role}_cwd",
|
|
)
|
|
_require(
|
|
errors,
|
|
target.get("git_sha") == expected_git_sha,
|
|
f"{prefix}:{role}_git_sha",
|
|
)
|
|
target_started = _iso(target.get("started_at"))
|
|
_require(
|
|
errors,
|
|
capture_started is not None
|
|
and target_started is not None
|
|
and target_started <= capture_started,
|
|
f"{prefix}:{role}_process_start",
|
|
)
|
|
_require(
|
|
errors,
|
|
len(target_pids) == 2 and len(set(target_pids)) == 2,
|
|
f"{prefix}:windows_distinct_pids",
|
|
)
|
|
|
|
completed = payload.get("samples_completed")
|
|
samples = payload.get("samples")
|
|
_require(
|
|
errors,
|
|
isinstance(samples, list)
|
|
and isinstance(completed, int)
|
|
and len(samples) == completed,
|
|
f"{prefix}:windows_samples",
|
|
)
|
|
capture_ended = _iso(payload.get("ended_at_utc"))
|
|
metric_values: dict[str, dict[str, list[float]]] = {
|
|
role: {
|
|
field: []
|
|
for field in (
|
|
"rss_bytes",
|
|
"peak_rss_bytes",
|
|
"cpu_time_seconds",
|
|
"cpu_percent",
|
|
"handles",
|
|
"threads",
|
|
)
|
|
}
|
|
for role in ("api", "cloudflared")
|
|
}
|
|
tcp_values: dict[str, dict[str, list[int]]] = {
|
|
role: {field: [] for field in ("connections", "established", "listeners")}
|
|
for role in ("api", "cloudflared")
|
|
}
|
|
listener_owned: list[int] = []
|
|
listener_conflicts: list[int] = []
|
|
host_connections: list[int] = []
|
|
if isinstance(samples, list):
|
|
previous_cpu: dict[str, float] = {}
|
|
for index, sample in enumerate(samples, start=1):
|
|
if not isinstance(sample, dict):
|
|
errors.append(f"{prefix}:windows_sample_shape")
|
|
continue
|
|
_require(
|
|
errors,
|
|
sample.get("sequence") == index,
|
|
f"{prefix}:windows_sample_sequence",
|
|
)
|
|
observed_at = _iso(sample.get("observed_at_utc"))
|
|
_require(
|
|
errors,
|
|
capture_started is not None
|
|
and capture_ended is not None
|
|
and observed_at is not None
|
|
and capture_started <= observed_at <= capture_ended,
|
|
f"{prefix}:windows_sample_time",
|
|
)
|
|
processes = sample.get("processes")
|
|
process_tcp = sample.get("process_tcp")
|
|
_require(
|
|
errors,
|
|
isinstance(processes, dict)
|
|
and set(processes) == {"api", "cloudflared"},
|
|
f"{prefix}:windows_sample_processes",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(process_tcp, dict)
|
|
and set(process_tcp) == {"api", "cloudflared"},
|
|
f"{prefix}:windows_sample_process_tcp",
|
|
)
|
|
for role in ("api", "cloudflared"):
|
|
metrics = processes.get(role) if isinstance(processes, dict) else None
|
|
tcp = process_tcp.get(role) if isinstance(process_tcp, dict) else None
|
|
_require(
|
|
errors,
|
|
isinstance(metrics, dict),
|
|
f"{prefix}:{role}_sample_metrics",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(tcp, dict),
|
|
f"{prefix}:{role}_sample_tcp",
|
|
)
|
|
if isinstance(metrics, dict):
|
|
for field in metric_values[role]:
|
|
numeric = _number(metrics.get(field))
|
|
positive = field != "cpu_percent"
|
|
_require(
|
|
errors,
|
|
numeric is not None
|
|
and numeric >= 0
|
|
and (not positive or numeric > 0),
|
|
f"{prefix}:{role}_{field}_sample",
|
|
)
|
|
if numeric is not None:
|
|
metric_values[role][field].append(numeric)
|
|
cpu_time = _number(metrics.get("cpu_time_seconds"))
|
|
if cpu_time is not None:
|
|
_require(
|
|
errors,
|
|
cpu_time >= previous_cpu.get(role, 0.0),
|
|
f"{prefix}:{role}_cpu_time_monotonic",
|
|
)
|
|
previous_cpu[role] = cpu_time
|
|
if isinstance(tcp, dict):
|
|
for field in tcp_values[role]:
|
|
value = tcp.get(field)
|
|
_require(
|
|
errors,
|
|
isinstance(value, int)
|
|
and not isinstance(value, bool)
|
|
and value >= 0,
|
|
f"{prefix}:{role}_tcp_{field}_sample",
|
|
)
|
|
if isinstance(value, int) and not isinstance(value, bool):
|
|
tcp_values[role][field].append(value)
|
|
if role == "api":
|
|
_require(
|
|
errors,
|
|
(_number(tcp.get("listeners")) or 0) > 0,
|
|
f"{prefix}:api_listener_sample",
|
|
)
|
|
else:
|
|
_require(
|
|
errors,
|
|
(_number(tcp.get("established")) or 0) > 0,
|
|
f"{prefix}:cloudflared_tunnel_sample",
|
|
)
|
|
listener = sample.get("api_listener")
|
|
host_tcp = sample.get("host_tcp")
|
|
_require(errors, isinstance(listener, dict), f"{prefix}:listener_sample")
|
|
_require(errors, isinstance(host_tcp, dict), f"{prefix}:host_tcp_sample")
|
|
if isinstance(listener, dict):
|
|
owned = listener.get("owned_listener_count")
|
|
conflicts = listener.get("conflicting_listener_count")
|
|
_require(
|
|
errors,
|
|
listener.get("port") == api_listen_port,
|
|
f"{prefix}:listener_port_sample",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(owned, int) and not isinstance(owned, bool) and owned > 0,
|
|
f"{prefix}:listener_owner_sample",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(conflicts, int)
|
|
and not isinstance(conflicts, bool)
|
|
and conflicts == 0,
|
|
f"{prefix}:listener_conflict_sample",
|
|
)
|
|
if isinstance(owned, int) and not isinstance(owned, bool):
|
|
listener_owned.append(owned)
|
|
if isinstance(conflicts, int) and not isinstance(conflicts, bool):
|
|
listener_conflicts.append(conflicts)
|
|
if isinstance(host_tcp, dict):
|
|
connections = host_tcp.get("connections")
|
|
_require(
|
|
errors,
|
|
isinstance(connections, int)
|
|
and not isinstance(connections, bool)
|
|
and connections > 0,
|
|
f"{prefix}:host_tcp_connections_sample",
|
|
)
|
|
if isinstance(connections, int) and not isinstance(connections, bool):
|
|
host_connections.append(connections)
|
|
|
|
summary = payload.get("summary")
|
|
_require(errors, isinstance(summary, dict), f"{prefix}:summary")
|
|
if isinstance(summary, dict):
|
|
process_summary = summary.get("processes")
|
|
listener_summary = summary.get("api_listener")
|
|
host_summary = summary.get("host_tcp")
|
|
_require(
|
|
errors,
|
|
isinstance(process_summary, dict)
|
|
and set(process_summary) == {"api", "cloudflared"},
|
|
f"{prefix}:windows_process_summary",
|
|
)
|
|
if isinstance(process_summary, dict):
|
|
for role in ("api", "cloudflared"):
|
|
values = process_summary.get(role)
|
|
_require(
|
|
errors,
|
|
isinstance(values, dict),
|
|
f"{prefix}:{role}_summary",
|
|
)
|
|
if not isinstance(values, dict):
|
|
continue
|
|
for field, observations in metric_values[role].items():
|
|
_require(
|
|
errors,
|
|
bool(observations)
|
|
and _number(values.get(f"{field}_max")) == max(observations),
|
|
f"{prefix}:{role}_{field}_max",
|
|
)
|
|
for field, observations in tcp_values[role].items():
|
|
_require(
|
|
errors,
|
|
bool(observations)
|
|
and _number(values.get(f"tcp_{field}_max"))
|
|
== max(observations),
|
|
f"{prefix}:{role}_tcp_{field}_max",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(listener_summary, dict)
|
|
and listener_summary.get("port") == api_listen_port
|
|
and bool(listener_owned)
|
|
and listener_summary.get("owned_listener_count_min")
|
|
== min(listener_owned)
|
|
and bool(listener_conflicts)
|
|
and listener_summary.get("conflicting_listener_count_max")
|
|
== max(listener_conflicts),
|
|
f"{prefix}:windows_listener_summary",
|
|
)
|
|
_require(
|
|
errors,
|
|
isinstance(host_summary, dict)
|
|
and bool(host_connections)
|
|
and host_summary.get("connections_max") == max(host_connections),
|
|
f"{prefix}:host_tcp",
|
|
)
|
|
|
|
|
|
def validate_topology(payload: dict[str, Any], errors: list[str]) -> None:
|
|
prefix = "topology"
|
|
_require(
|
|
errors,
|
|
payload.get("schema_version") == "vignette.g7-topology-evidence.v1",
|
|
f"{prefix}:schema",
|
|
)
|
|
_require(errors, payload.get("status") == "passed", f"{prefix}:status")
|
|
scope = payload.get("scope")
|
|
_require(
|
|
errors,
|
|
isinstance(scope, dict) and scope.get("metadata_only") is True,
|
|
f"{prefix}:privacy",
|
|
)
|
|
if isinstance(scope, dict):
|
|
for field in (
|
|
"raw_command_output_retained",
|
|
"socket_endpoints_retained",
|
|
"request_payloads_retained",
|
|
"audio_retained",
|
|
"transcripts_retained",
|
|
):
|
|
_require(errors, scope.get(field) is False, f"{prefix}:{field}")
|
|
edge = scope.get("cloudflare_edge")
|
|
_require(
|
|
errors,
|
|
isinstance(edge, dict)
|
|
and edge.get("internal_queue_measured") is False
|
|
and edge.get("evidence_boundary") == "separate_external_artifact_required",
|
|
f"{prefix}:edge_boundary",
|
|
)
|
|
requested = payload.get("requested")
|
|
completed = payload.get("samples_completed")
|
|
_require(errors, isinstance(requested, dict), f"{prefix}:requested")
|
|
if isinstance(requested, dict):
|
|
sample_count = requested.get("samples")
|
|
interval = _number(requested.get("interval_seconds"))
|
|
_require(
|
|
errors,
|
|
isinstance(sample_count, int)
|
|
and sample_count == completed
|
|
and sample_count > 1,
|
|
f"{prefix}:sample_count",
|
|
)
|
|
_require(
|
|
errors,
|
|
interval is not None
|
|
and isinstance(completed, int)
|
|
and (completed - 1) * interval >= MINIMUM_SOAK_SECONDS,
|
|
f"{prefix}:coverage",
|
|
)
|
|
topology_mode = payload.get("topology_mode")
|
|
_require(
|
|
errors,
|
|
topology_mode in (None, "linux_compose", "windows_host"),
|
|
f"{prefix}:mode",
|
|
)
|
|
if topology_mode == "windows_host":
|
|
_require(
|
|
errors,
|
|
isinstance(scope, dict)
|
|
and scope.get("topology_boundary")
|
|
== "windows_host_api_and_cloudflared_processes"
|
|
and scope.get("configured_listener_port_retained") is True,
|
|
f"{prefix}:windows_scope",
|
|
)
|
|
_validate_windows_topology(payload, errors)
|
|
elif topology_mode in (None, "linux_compose"):
|
|
_validate_linux_compose_topology(payload, errors)
|
|
_require(errors, payload.get("failure_type") is None, f"{prefix}:failure")
|
|
|
|
|
|
def validate_human_gain(payload: dict[str, Any], errors: list[str]) -> dict[str, Any]:
|
|
try:
|
|
pack = G7HumanVoiceGainEvidencePack.model_validate(payload)
|
|
result = evaluate_human_voice_gain(pack)
|
|
except Exception as exc:
|
|
errors.append(f"human_gain:invalid:{type(exc).__name__}")
|
|
return {}
|
|
if not result.passed:
|
|
errors.extend(f"human_gain:{reason}" for reason in result.failure_reasons)
|
|
return result.model_dump(mode="json")
|
|
|
|
|
|
def validate_binding(
|
|
voice: dict[str, Any],
|
|
runtime: dict[str, Any],
|
|
topology: dict[str, Any],
|
|
errors: list[str],
|
|
) -> None:
|
|
voice_host = voice.get("target_host")
|
|
runtime_host = runtime.get("target_host")
|
|
requested = topology.get("requested")
|
|
topology_host = (
|
|
requested.get("public_host") if isinstance(requested, dict) else None
|
|
)
|
|
_require(
|
|
errors,
|
|
bool(voice_host) and voice_host == runtime_host == topology_host,
|
|
"binding:public_host",
|
|
)
|
|
windows = []
|
|
for label, payload in (
|
|
("voice", voice),
|
|
("runtime", runtime),
|
|
("topology", topology),
|
|
):
|
|
start, end = (
|
|
_iso(payload.get("started_at_utc")),
|
|
_iso(payload.get("ended_at_utc")),
|
|
)
|
|
if start is None or end is None or end < start:
|
|
errors.append(f"binding:{label}_time")
|
|
else:
|
|
windows.append((start, end))
|
|
if len(windows) == 3:
|
|
overlap_started = max(start for start, _ in windows)
|
|
overlap_ended = min(end for _, end in windows)
|
|
_require(
|
|
errors,
|
|
overlap_started <= overlap_ended,
|
|
"binding:no_concurrent_overlap",
|
|
)
|
|
if overlap_started <= overlap_ended:
|
|
_require(
|
|
errors,
|
|
(overlap_ended - overlap_started).total_seconds()
|
|
>= MINIMUM_SOAK_SECONDS,
|
|
"binding:concurrent_overlap_below_3000_seconds",
|
|
)
|
|
|
|
|
|
def artifact_sha256(path: Path) -> str:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def parser() -> argparse.ArgumentParser:
|
|
result = argparse.ArgumentParser(description=__doc__)
|
|
result.add_argument("--voice-soak", type=Path, required=True)
|
|
result.add_argument("--runtime", type=Path, required=True)
|
|
result.add_argument("--topology", type=Path, required=True)
|
|
result.add_argument("--human-voice-gain", type=Path, required=True)
|
|
result.add_argument("--json", action="store_true")
|
|
return result
|
|
|
|
|
|
def main() -> int:
|
|
args = parser().parse_args()
|
|
errors: list[str] = []
|
|
paths = {
|
|
"voice_soak": args.voice_soak,
|
|
"runtime": args.runtime,
|
|
"topology": args.topology,
|
|
"human_voice_gain": args.human_voice_gain,
|
|
}
|
|
payloads = {label: load_json(path, errors, label) for label, path in paths.items()}
|
|
if payloads["voice_soak"]:
|
|
validate_public_soak(payloads["voice_soak"], errors)
|
|
if payloads["runtime"]:
|
|
validate_runtime(payloads["runtime"], errors)
|
|
if payloads["topology"]:
|
|
validate_topology(payloads["topology"], errors)
|
|
gain_result = (
|
|
validate_human_gain(payloads["human_voice_gain"], errors)
|
|
if payloads["human_voice_gain"]
|
|
else {}
|
|
)
|
|
if all(payloads[label] for label in ("voice_soak", "runtime", "topology")):
|
|
validate_binding(
|
|
payloads["voice_soak"], payloads["runtime"], payloads["topology"], errors
|
|
)
|
|
report = {
|
|
"schema_version": "vignette.g7-external-proof-result.v1",
|
|
"passed": not errors,
|
|
"clinical_claim_allowed": False,
|
|
"artifact_sha256": {
|
|
label: artifact_sha256(path)
|
|
for label, path in paths.items()
|
|
if path.is_file()
|
|
},
|
|
"human_voice_gain_result": gain_result,
|
|
"errors": errors,
|
|
}
|
|
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
|
|
return 0 if not errors else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|