feat(perception): integrate calibrated operator pipeline
Add calibrated K1 projection, recorded and near-live perception qualification, unified Rerun operator layers, bounded replay admission, audited viewer controls, worker experiments, and lab evidence.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
"""Host-owned network adapters used by device plugins."""
|
||||
|
||||
from k1link.host_network.wifi import (
|
||||
HostWifiCredentialMaterialAvailabilityResult,
|
||||
HostWifiCredentialMaterialStoreResult,
|
||||
HostWifiProfileError,
|
||||
associate_with_wifi_profile_once,
|
||||
check_wifi_credential_material,
|
||||
check_wifi_profile,
|
||||
ensure_wifi_profile_from_credential_source,
|
||||
store_wifi_credential_material,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"HostWifiCredentialMaterialAvailabilityResult",
|
||||
"HostWifiCredentialMaterialStoreResult",
|
||||
"HostWifiProfileError",
|
||||
"associate_with_wifi_profile_once",
|
||||
"check_wifi_credential_material",
|
||||
"check_wifi_profile",
|
||||
"ensure_wifi_profile_from_credential_source",
|
||||
"store_wifi_credential_material",
|
||||
]
|
||||
@@ -0,0 +1,429 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal, TypedDict
|
||||
|
||||
|
||||
class HostWifiProfileAssociationResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
outcome: str
|
||||
already_associated: bool
|
||||
profile_enrolled: bool
|
||||
scan_attempt_count: int
|
||||
scan_elapsed_ms: int
|
||||
credential_source: str
|
||||
|
||||
|
||||
class HostWifiProfileStoreResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
outcome: Literal["stored"]
|
||||
|
||||
|
||||
class HostWifiProfileAvailabilityResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
available: bool
|
||||
|
||||
|
||||
class HostWifiProfileEnsureResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
available: bool
|
||||
profile_enrolled: bool
|
||||
credential_source: str | None
|
||||
|
||||
|
||||
class HostWifiCredentialMaterialStoreResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
outcome: Literal["stored"]
|
||||
|
||||
|
||||
class HostWifiCredentialMaterialAvailabilityResult(TypedDict):
|
||||
schema_version: int
|
||||
adapter: str
|
||||
available: bool
|
||||
|
||||
|
||||
_ERROR_MESSAGES = {
|
||||
"unsupported-platform": (
|
||||
"для этой ОС ещё не установлен адаптер системных Wi-Fi-профилей"
|
||||
),
|
||||
"profile-unavailable": (
|
||||
"реквизиты выбранного K1 не заведены на этом управляющем устройстве"
|
||||
),
|
||||
"credential-source-unavailable": (
|
||||
"credential provider точной версии прошивки не установлен на этом устройстве"
|
||||
),
|
||||
"network-not-found": (
|
||||
"точка доступа выбранного K1 не найдена; проверьте режим K1 и питание"
|
||||
),
|
||||
"profile-ssid-mismatch": "сохранённый профиль принадлежит другой точке доступа",
|
||||
"credential-entry-cancelled": "ввод пароля точки доступа K1 отменён оператором",
|
||||
"credential-invalid": "пароль точки доступа K1 имеет недопустимую длину",
|
||||
"host-wifi-operation-timeout": (
|
||||
"оператор не завершил системное подключение Wi-Fi за отведённое время"
|
||||
),
|
||||
"wifi-interface-unavailable": "системный Wi-Fi-интерфейс недоступен",
|
||||
"corewlan-error": "системный Wi-Fi не смог подключиться к точке доступа K1",
|
||||
}
|
||||
|
||||
|
||||
class HostWifiProfileError(RuntimeError):
|
||||
"""One bounded host-side Wi-Fi profile operation failed."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
reason_code: str,
|
||||
*,
|
||||
scan_attempt_count: int | None = None,
|
||||
scan_elapsed_ms: int | None = None,
|
||||
) -> None:
|
||||
self.reason_code = reason_code
|
||||
self.scan_attempt_count = scan_attempt_count
|
||||
self.scan_elapsed_ms = scan_elapsed_ms
|
||||
message = _ERROR_MESSAGES.get(reason_code, "операция системного Wi-Fi завершилась ошибкой")
|
||||
super().__init__(f"{message} ({reason_code})")
|
||||
|
||||
|
||||
RunProcess = Callable[..., subprocess.CompletedProcess[bytes]]
|
||||
|
||||
|
||||
def _validate_profile_id(profile_id: str) -> None:
|
||||
if not 1 <= len(profile_id) <= 128:
|
||||
raise ValueError("host Wi-Fi profile id must contain between 1 and 128 characters")
|
||||
if not all(
|
||||
character.isascii() and (character.isalnum() or character in "._-")
|
||||
for character in profile_id
|
||||
):
|
||||
raise ValueError("host Wi-Fi profile id contains unsupported characters")
|
||||
|
||||
|
||||
def _run_macos_helper(
|
||||
helper_path: Path,
|
||||
request: dict[str, object],
|
||||
*,
|
||||
timeout_seconds: float,
|
||||
runner: RunProcess,
|
||||
) -> dict[str, Any]:
|
||||
if sys.platform != "darwin":
|
||||
raise HostWifiProfileError("unsupported-platform")
|
||||
if not helper_path.is_file():
|
||||
raise HostWifiProfileError("host-wifi-helper-missing")
|
||||
if timeout_seconds <= 0:
|
||||
raise ValueError("timeout_seconds must be positive")
|
||||
|
||||
request_bytes = bytearray(
|
||||
json.dumps(request, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||||
)
|
||||
try:
|
||||
completed = runner(
|
||||
["/usr/bin/xcrun", "swift", str(helper_path.resolve())],
|
||||
input=request_bytes,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=timeout_seconds,
|
||||
check=False,
|
||||
)
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
raise HostWifiProfileError("host-wifi-operation-timeout") from exc
|
||||
except OSError as exc:
|
||||
raise HostWifiProfileError("host-wifi-helper-unavailable") from exc
|
||||
finally:
|
||||
request_bytes[:] = b"\x00" * len(request_bytes)
|
||||
|
||||
if len(completed.stdout) > 4096:
|
||||
raise HostWifiProfileError("host-wifi-response-too-large")
|
||||
try:
|
||||
response: Any = json.loads(completed.stdout.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
raise HostWifiProfileError("host-wifi-response-invalid") from exc
|
||||
if not isinstance(response, dict):
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
|
||||
reason_code = response.get("reason_code")
|
||||
if completed.returncode != 0 or response.get("ok") is not True:
|
||||
if not isinstance(reason_code, str) or not reason_code:
|
||||
reason_code = "host-wifi-operation-failed"
|
||||
scan_attempt_count = response.get("scan_attempt_count")
|
||||
scan_elapsed_ms = response.get("scan_elapsed_ms")
|
||||
raise HostWifiProfileError(
|
||||
reason_code,
|
||||
scan_attempt_count=(
|
||||
scan_attempt_count
|
||||
if type(scan_attempt_count) is int and scan_attempt_count >= 1
|
||||
else None
|
||||
),
|
||||
scan_elapsed_ms=(
|
||||
scan_elapsed_ms
|
||||
if type(scan_elapsed_ms) is int and scan_elapsed_ms >= 0
|
||||
else None
|
||||
),
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def associate_with_wifi_profile_once(
|
||||
helper_path: Path,
|
||||
profile_id: str,
|
||||
expected_ssid: str,
|
||||
*,
|
||||
scan_timeout_seconds: float = 15.0,
|
||||
timeout_seconds: float = 180.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiProfileAssociationResult:
|
||||
"""Associate through one device-scoped OS profile without exposing its secret.
|
||||
|
||||
The selected device supplies the expected, operator-visible AP SSID. The
|
||||
platform helper performs bounded exact-SSID discovery followed by at most
|
||||
one association. It resolves a device-scoped secret from the OS credential
|
||||
stores, or asks for it through a native secure prompt on first use. The
|
||||
secret never crosses the helper boundary.
|
||||
"""
|
||||
|
||||
_validate_profile_id(profile_id)
|
||||
if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
|
||||
raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
|
||||
if not 0 <= scan_timeout_seconds <= 60:
|
||||
raise ValueError("scan_timeout_seconds must be between 0 and 60")
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "associate",
|
||||
"profile_id": profile_id,
|
||||
"ssid": expected_ssid,
|
||||
"scan_timeout_seconds": scan_timeout_seconds,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
already_associated = response.get("already_associated")
|
||||
profile_enrolled = response.get("profile_enrolled")
|
||||
scan_attempt_count = response.get("scan_attempt_count")
|
||||
scan_elapsed_ms = response.get("scan_elapsed_ms")
|
||||
credential_source = response.get("credential_source")
|
||||
adapter = response.get("adapter")
|
||||
if (
|
||||
not isinstance(already_associated, bool)
|
||||
or not isinstance(profile_enrolled, bool)
|
||||
or not isinstance(adapter, str)
|
||||
or not adapter
|
||||
or type(scan_attempt_count) is not int
|
||||
or scan_attempt_count < 1
|
||||
or type(scan_elapsed_ms) is not int
|
||||
or scan_elapsed_ms < 0
|
||||
or not isinstance(credential_source, str)
|
||||
or credential_source not in {
|
||||
"mission-core-keychain",
|
||||
"system-wifi-keychain",
|
||||
"native-secure-prompt",
|
||||
"exact-firmware-profile",
|
||||
}
|
||||
):
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"outcome": "already-associated" if already_associated else "associated",
|
||||
"already_associated": already_associated,
|
||||
"profile_enrolled": profile_enrolled,
|
||||
"scan_attempt_count": scan_attempt_count,
|
||||
"scan_elapsed_ms": scan_elapsed_ms,
|
||||
"credential_source": credential_source,
|
||||
}
|
||||
|
||||
|
||||
def check_wifi_profile(
|
||||
helper_path: Path,
|
||||
profile_id: str,
|
||||
expected_ssid: str,
|
||||
*,
|
||||
timeout_seconds: float = 30.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiProfileAvailabilityResult:
|
||||
"""Check one device-scoped Keychain profile without scanning or joining Wi-Fi."""
|
||||
|
||||
_validate_profile_id(profile_id)
|
||||
if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
|
||||
raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "check-profile",
|
||||
"profile_id": profile_id,
|
||||
"ssid": expected_ssid,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
available = response.get("profile_available")
|
||||
adapter = response.get("adapter")
|
||||
if not isinstance(available, bool) or not isinstance(adapter, str) or not adapter:
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"available": available,
|
||||
}
|
||||
|
||||
|
||||
def ensure_wifi_profile_from_credential_source(
|
||||
helper_path: Path,
|
||||
profile_id: str,
|
||||
expected_ssid: str,
|
||||
credential_source_id: str,
|
||||
*,
|
||||
timeout_seconds: float = 30.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiProfileEnsureResult:
|
||||
"""Materialize one device profile from a firmware-scoped secure-store item.
|
||||
|
||||
Only opaque identifiers and the operator-visible SSID cross the platform
|
||||
helper boundary. The credential stays inside the OS credential store.
|
||||
"""
|
||||
|
||||
_validate_profile_id(profile_id)
|
||||
_validate_profile_id(credential_source_id)
|
||||
if not 1 <= len(expected_ssid.encode("utf-8")) <= 32:
|
||||
raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "ensure-profile",
|
||||
"profile_id": profile_id,
|
||||
"ssid": expected_ssid,
|
||||
"credential_source_id": credential_source_id,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
available = response.get("profile_available")
|
||||
profile_enrolled = response.get("profile_enrolled")
|
||||
credential_source = response.get("credential_source")
|
||||
adapter = response.get("adapter")
|
||||
if (
|
||||
not isinstance(available, bool)
|
||||
or not isinstance(profile_enrolled, bool)
|
||||
or not isinstance(adapter, str)
|
||||
or not adapter
|
||||
or (credential_source is not None and not isinstance(credential_source, str))
|
||||
):
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"available": available,
|
||||
"profile_enrolled": profile_enrolled,
|
||||
"credential_source": credential_source,
|
||||
}
|
||||
|
||||
|
||||
def store_wifi_credential_material(
|
||||
helper_path: Path,
|
||||
credential_source_id: str,
|
||||
password: str,
|
||||
*,
|
||||
timeout_seconds: float = 30.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiCredentialMaterialStoreResult:
|
||||
"""Store firmware-scoped material without putting it in argv or output."""
|
||||
|
||||
_validate_profile_id(credential_source_id)
|
||||
if not 8 <= len(password.encode("utf-8")) <= 63:
|
||||
raise ValueError("WPA-PSK must contain between 8 and 63 UTF-8 bytes")
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "store-credential-material",
|
||||
"profile_id": credential_source_id,
|
||||
"password": password,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
adapter = response.get("adapter")
|
||||
if response.get("stored") is not True or not isinstance(adapter, str) or not adapter:
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"outcome": "stored",
|
||||
}
|
||||
|
||||
|
||||
def check_wifi_credential_material(
|
||||
helper_path: Path,
|
||||
credential_source_id: str,
|
||||
*,
|
||||
timeout_seconds: float = 30.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiCredentialMaterialAvailabilityResult:
|
||||
"""Check one opaque firmware provider item without loading its secret."""
|
||||
|
||||
_validate_profile_id(credential_source_id)
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "check-credential-material",
|
||||
"profile_id": credential_source_id,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
available = response.get("profile_available")
|
||||
adapter = response.get("adapter")
|
||||
if not isinstance(available, bool) or not isinstance(adapter, str) or not adapter:
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"available": available,
|
||||
}
|
||||
|
||||
|
||||
def store_wifi_profile_once(
|
||||
helper_path: Path,
|
||||
profile_id: str,
|
||||
ssid: str,
|
||||
password: str,
|
||||
*,
|
||||
timeout_seconds: float = 30.0,
|
||||
runner: RunProcess = subprocess.run,
|
||||
) -> HostWifiProfileStoreResult:
|
||||
"""Store one profile in the current user's secure OS credential store.
|
||||
|
||||
This is an explicit local administration boundary. The secret is carried
|
||||
only in the helper's stdin and is never placed in argv, the environment,
|
||||
stdout, stderr, an evidence manifest, or source control.
|
||||
"""
|
||||
|
||||
_validate_profile_id(profile_id)
|
||||
if not 1 <= len(ssid.encode("utf-8")) <= 32:
|
||||
raise ValueError("SSID must contain between 1 and 32 UTF-8 bytes")
|
||||
if not 1 <= len(password.encode("utf-8")) <= 64:
|
||||
raise ValueError("Wi-Fi password must contain between 1 and 64 UTF-8 bytes")
|
||||
response = _run_macos_helper(
|
||||
helper_path,
|
||||
{
|
||||
"action": "store-profile",
|
||||
"profile_id": profile_id,
|
||||
"ssid": ssid,
|
||||
"password": password,
|
||||
},
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
)
|
||||
adapter = response.get("adapter")
|
||||
if response.get("stored") is not True or not isinstance(adapter, str) or not adapter:
|
||||
raise HostWifiProfileError("host-wifi-response-invalid")
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"adapter": adapter,
|
||||
"outcome": "stored",
|
||||
}
|
||||
Reference in New Issue
Block a user