NODEDC_MISSION_CORE/plugins/xgrids-k1/profile_loader.py

487 lines
20 KiB
Python

"""Strict loader for the local XGRIDS K1 compatibility profile.
The profile is descriptive and fail-closed. Loading it never performs device
I/O and never grants write authority to a transport implementation.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
PROFILE_SCHEMA_VERSION = 1
DEFAULT_PROFILE_ID = "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1"
DEFAULT_PROFILE_PATH = Path(__file__).parent / "profiles" / "fw-3.0.2" / "direct-lan.v1.json"
EVIDENCE_FLAGS = (
"observed",
"decoded",
"replay_verified",
"physical_verified",
"write_enabled",
)
class CompatibilityProfileError(ValueError):
"""The compatibility profile is malformed or exceeds its reviewed scope."""
def _object(value: Any, path: str) -> dict[str, Any]:
if not isinstance(value, dict):
raise CompatibilityProfileError(f"{path} must be an object")
return value
def _array(value: Any, path: str) -> list[Any]:
if not isinstance(value, list):
raise CompatibilityProfileError(f"{path} must be an array")
return value
def _string(value: Any, path: str) -> str:
if not isinstance(value, str) or not value:
raise CompatibilityProfileError(f"{path} must be a non-empty string")
return value
def _unique_index(items: list[Any], path: str) -> dict[str, dict[str, Any]]:
index: dict[str, dict[str, Any]] = {}
for position, value in enumerate(items):
item_path = f"{path}[{position}]"
item = _object(value, item_path)
item_id = _string(item.get("id"), f"{item_path}.id")
if item_id in index:
raise CompatibilityProfileError(f"{path} contains duplicate id {item_id!r}")
index[item_id] = item
return index
def _validate_evidence(value: Any, path: str) -> dict[str, bool]:
evidence = _object(value, path)
if set(evidence) != set(EVIDENCE_FLAGS):
expected = ", ".join(EVIDENCE_FLAGS)
raise CompatibilityProfileError(f"{path} must contain exactly: {expected}")
for flag in EVIDENCE_FLAGS:
if not isinstance(evidence[flag], bool):
raise CompatibilityProfileError(f"{path}.{flag} must be a boolean")
if evidence["write_enabled"]:
raise CompatibilityProfileError(f"{path}.write_enabled must remain false in v1")
return evidence # type: ignore[return-value]
def _walk_and_validate_evidence(value: Any, path: str = "$") -> None:
if isinstance(value, dict):
if "evidence" in value:
_validate_evidence(value["evidence"], f"{path}.evidence")
for key, child in value.items():
child_path = f"{path}.{key}"
if key == "write_enabled" and path != "$.evidence_vocabulary":
if not isinstance(child, bool):
raise CompatibilityProfileError(f"{child_path} must be a boolean")
if child:
raise CompatibilityProfileError(f"{child_path} must remain false in v1")
_walk_and_validate_evidence(child, child_path)
elif isinstance(value, list):
for position, child in enumerate(value):
_walk_and_validate_evidence(child, f"{path}[{position}]")
def _expect_evidence(
item: dict[str, Any],
path: str,
*,
observed: bool,
decoded: bool,
replay_verified: bool,
physical_verified: bool,
) -> None:
actual = _validate_evidence(item.get("evidence"), f"{path}.evidence")
expected = {
"observed": observed,
"decoded": decoded,
"replay_verified": replay_verified,
"physical_verified": physical_verified,
"write_enabled": False,
}
if actual != expected:
raise CompatibilityProfileError(
f"{path}.evidence exceeds or contradicts the reviewed v1 evidence"
)
def _validate_sources(profile: dict[str, Any]) -> set[str]:
sources = _unique_index(
_array(profile.get("evidence_sources"), "$.evidence_sources"),
"$.evidence_sources",
)
for source_id, source in sources.items():
_string(source.get("kind"), f"$.evidence_sources[{source_id!r}].kind")
path = _string(source.get("path"), f"$.evidence_sources[{source_id!r}].path")
if path.startswith("/") or ".." in Path(path).parts:
raise CompatibilityProfileError(
f"$.evidence_sources[{source_id!r}].path must be repository-relative"
)
_string(source.get("scope"), f"$.evidence_sources[{source_id!r}].scope")
return set(sources)
def _validate_source_references(value: Any, source_ids: set[str], path: str = "$") -> None:
if isinstance(value, dict):
if "source_ids" in value:
references = _array(value["source_ids"], f"{path}.source_ids")
if len(references) != len(set(references)):
raise CompatibilityProfileError(f"{path}.source_ids contains duplicates")
for position, source_id in enumerate(references):
source_id = _string(source_id, f"{path}.source_ids[{position}]")
if source_id not in source_ids:
raise CompatibilityProfileError(
f"{path}.source_ids[{position}] references unknown evidence source"
)
for key, child in value.items():
_validate_source_references(child, source_ids, f"{path}.{key}")
elif isinstance(value, list):
for position, child in enumerate(value):
_validate_source_references(child, source_ids, f"{path}[{position}]")
def _validate_transports(profile: dict[str, Any]) -> None:
transports = _unique_index(
_array(profile.get("transports"), "$.transports"),
"$.transports",
)
if set(transports) != {
"ble.wifi-bootstrap.fw3.v1",
"mqtt.direct-lan.fw3.v1",
"rtsp.camera-preview.fw3.v1",
}:
raise CompatibilityProfileError("$.transports must contain only the reviewed v1 transports")
ble = transports["ble.wifi-bootstrap.fw3.v1"]
if ble.get("service_uuid") != "00007f00-0000-1000-8000-00805f9b34fb":
raise CompatibilityProfileError("BLE service UUID differs from reviewed evidence")
characteristics = _object(ble.get("characteristics"), "$.transports[ble].characteristics")
if characteristics != {
"wifi_request": "00007f01-0000-1000-8000-00805f9b34fb",
"wifi_status": "00007f02-0000-1000-8000-00805f9b34fb",
}:
raise CompatibilityProfileError("BLE characteristic UUIDs differ from reviewed evidence")
if ble.get("request_frame_bytes") != 99:
raise CompatibilityProfileError("BLE provisioning frame must remain exactly 99 bytes")
_expect_evidence(
ble,
"$.transports[ble.wifi-bootstrap.fw3.v1]",
observed=True,
decoded=True,
replay_verified=False,
physical_verified=True,
)
mqtt = transports["mqtt.direct-lan.fw3.v1"]
if mqtt.get("protocol") != "MQTT 3.1.1":
raise CompatibilityProfileError("direct-LAN application protocol must remain MQTT 3.1.1")
network = _object(mqtt.get("network"), "$.transports[mqtt].network")
if network.get("transport") != "TCP" or network.get("port") != 1883:
raise CompatibilityProfileError("direct-LAN MQTT endpoint must remain TCP 1883")
if network.get("tls") is not False or network.get("authentication") != "none-observed":
raise CompatibilityProfileError("direct-LAN MQTT security claim differs from observation")
allowlist = _array(
mqtt.get("subscription_allowlist"),
"$.transports[mqtt].subscription_allowlist",
)
if "lixel/application/report/#" not in allowlist:
raise CompatibilityProfileError("MQTT report-topic allowlist is missing")
for topic in allowlist:
topic = _string(topic, "$.transports[mqtt].subscription_allowlist[]")
if "/request/" in topic:
raise CompatibilityProfileError(
"request topics cannot enter the subscribe-only profile"
)
_expect_evidence(
mqtt,
"$.transports[mqtt.direct-lan.fw3.v1]",
observed=True,
decoded=False,
replay_verified=False,
physical_verified=True,
)
rtsp = transports["rtsp.camera-preview.fw3.v1"]
if rtsp.get("protocol") != "RTSP 1.0 with interleaved RTP over TCP":
raise CompatibilityProfileError("camera-preview protocol differs from observation")
rtsp_network = _object(rtsp.get("network"), "$.transports[rtsp].network")
if rtsp_network.get("transport") != "TCP" or rtsp_network.get("port") != 8554:
raise CompatibilityProfileError("camera-preview endpoint must remain TCP 8554")
if (
rtsp_network.get("tls") is not False
or rtsp_network.get("authentication") != "none-observed"
):
raise CompatibilityProfileError("camera-preview security differs from observation")
media = _object(rtsp.get("media"), "$.transports[rtsp].media")
if media != {
"codec": "H.264",
"rtp_payload_type": 96,
"clock_hz": 90000,
"framing": "RTP/AVP/TCP interleaved channels 0-1",
}:
raise CompatibilityProfileError("camera-preview media contract differs from observation")
_expect_evidence(
rtsp,
"$.transports[rtsp.camera-preview.fw3.v1]",
observed=True,
decoded=False,
replay_verified=False,
physical_verified=True,
)
def _validate_channels(profile: dict[str, Any]) -> None:
channels = _unique_index(_array(profile.get("channels"), "$.channels"), "$.channels")
expected_ids = {
"spatial.point-cloud.live",
"spatial.pose.live",
"device.status.live",
"device.heartbeat.live",
"camera.preview.live",
}
if set(channels) != expected_ids:
raise CompatibilityProfileError("$.channels differs from the reviewed v1 channel set")
point = channels["spatial.point-cloud.live"]
if point.get("topic") != "lixel/application/report/lio_pcl":
raise CompatibilityProfileError("point-cloud topic differs from reviewed evidence")
_expect_evidence(
point,
"$.channels[spatial.point-cloud.live]",
observed=True,
decoded=True,
replay_verified=True,
physical_verified=True,
)
pose = channels["spatial.pose.live"]
if pose.get("topic") != "lixel/application/report/lio_pose":
raise CompatibilityProfileError("pose topic differs from reviewed evidence")
_expect_evidence(
pose,
"$.channels[spatial.pose.live]",
observed=True,
decoded=True,
replay_verified=True,
physical_verified=True,
)
for channel_id, topic in (
("device.status.live", "lixel/application/report/device_status"),
("device.heartbeat.live", "lixel/application/report/heartbeat"),
):
channel = channels[channel_id]
if channel.get("topic") != topic or channel.get("semantic_payload") is not None:
raise CompatibilityProfileError(f"{channel_id} must remain raw-only in v1")
_expect_evidence(
channel,
f"$.channels[{channel_id}]",
observed=True,
decoded=False,
replay_verified=False,
physical_verified=True,
)
camera = channels["camera.preview.live"]
if camera.get("discovery_status") != "observed":
raise CompatibilityProfileError("camera discovery must match the owner-controlled run")
if camera.get("topic") is not None:
raise CompatibilityProfileError("camera preview is not an MQTT topic")
expected_endpoints = {
"rtsp://{confirmed-device-private-ipv4}:8554/live/chn_left_main",
"rtsp://{confirmed-device-private-ipv4}:8554/live/chn_right_main",
}
endpoints = _array(camera.get("endpoint_templates"), "$.channels[camera].endpoint_templates")
if set(endpoints) != expected_endpoints or len(endpoints) != len(expected_endpoints):
raise CompatibilityProfileError("camera endpoint templates differ from observation")
if camera.get("wire_format") != "RTSP 1.0, interleaved RTP/TCP, H.264 PT96 at 90000 Hz":
raise CompatibilityProfileError("camera wire format differs from observation")
_string(camera.get("semantic_payload"), "$.channels[camera].semantic_payload")
_expect_evidence(
camera,
"$.channels[camera.preview.live]",
observed=True,
decoded=False,
replay_verified=False,
physical_verified=True,
)
def _validate_acquisition_control(profile: dict[str, Any]) -> None:
control = _object(profile.get("acquisition_control"), "$.acquisition_control")
if control.get("mode") != "operator-manual" or control.get("write_enabled") is not False:
raise CompatibilityProfileError("acquisition control must remain operator-manual")
device_control = _object(
control.get("verified_device_control"),
"$.acquisition_control.verified_device_control",
)
if device_control.get("gesture") != "physical-double-click":
raise CompatibilityProfileError("physical acquisition gesture differs from lab evidence")
_expect_evidence(
device_control,
"$.acquisition_control.verified_device_control",
observed=True,
decoded=False,
replay_verified=False,
physical_verified=True,
)
actions = _unique_index(
_array(control.get("semantic_actions"), "$.acquisition_control.semantic_actions"),
"$.acquisition_control.semantic_actions",
)
if set(actions) != {
"acquisition.start",
"acquisition.stop",
"calibration.device.start",
}:
raise CompatibilityProfileError("semantic action set differs from reviewed v1")
for action_id, action_value in (("acquisition.start", 1), ("acquisition.stop", 2)):
action = actions[action_id]
if action.get("execution") != "operator-manual":
raise CompatibilityProfileError(f"{action_id} must remain operator-manual")
mapping = _object(
action.get("vendor_request_mapping"),
f"$.acquisition_control.semantic_actions[{action_id}].vendor_request_mapping",
)
if mapping.get("evidence_kind") != "owner-controlled-wire-observation":
raise CompatibilityProfileError(f"{action_id} vendor mapping differs from evidence")
if mapping.get("topic") != "lixel/application/request/modeling":
raise CompatibilityProfileError(
f"{action_id} vendor topic differs from static evidence"
)
if mapping.get("qos") != 2 or mapping.get("action_field_value") != action_value:
raise CompatibilityProfileError(
f"{action_id} vendor mapping differs from static evidence"
)
if not _array(
mapping.get("required_unresolved_context"),
f"$.acquisition_control.semantic_actions[{action_id}].required_unresolved_context",
):
raise CompatibilityProfileError(f"{action_id} must declare unresolved request context")
_expect_evidence(
mapping,
f"$.acquisition_control.semantic_actions[{action_id}].vendor_request_mapping",
observed=True,
decoded=True,
replay_verified=False,
physical_verified=True,
)
_expect_evidence(
action,
f"$.acquisition_control.semantic_actions[{action_id}]",
observed=True,
decoded=True,
replay_verified=False,
physical_verified=True,
)
calibration = actions["calibration.device.start"]
if calibration.get("execution") != "unavailable":
raise CompatibilityProfileError("calibration must remain unavailable in v1")
if calibration.get("vendor_request_mapping") is not None:
raise CompatibilityProfileError("calibration vendor request is not evidenced")
_expect_evidence(
calibration,
"$.acquisition_control.semantic_actions[calibration.device.start]",
observed=False,
decoded=False,
replay_verified=False,
physical_verified=False,
)
def validate_compatibility_profile(profile: Any) -> dict[str, Any]:
"""Validate and return one read-only firmware-3/direct-LAN profile object."""
root = _object(profile, "$")
if root.get("schema_version") != PROFILE_SCHEMA_VERSION:
raise CompatibilityProfileError("unsupported compatibility profile schema_version")
if root.get("profile_id") != DEFAULT_PROFILE_ID:
raise CompatibilityProfileError("unexpected compatibility profile_id")
scope = _object(root.get("scope"), "$.scope")
firmware = _object(scope.get("firmware"), "$.scope.firmware")
if firmware != {"match": "exact", "version": "3.0.2"}:
raise CompatibilityProfileError("profile must match firmware 3.0.2 exactly")
if scope.get("topology") != "direct-lan":
raise CompatibilityProfileError("profile topology must remain direct-lan")
vocabulary = _object(root.get("evidence_vocabulary"), "$.evidence_vocabulary")
if set(vocabulary) != set(EVIDENCE_FLAGS):
raise CompatibilityProfileError("evidence vocabulary differs from schema v1")
for flag in EVIDENCE_FLAGS:
_string(vocabulary[flag], f"$.evidence_vocabulary.{flag}")
safety = _object(root.get("safety"), "$.safety")
if safety.get("default_mode") != "read-only":
raise CompatibilityProfileError("profile default mode must remain read-only")
if safety.get("vendor_writes_enabled") is not False:
raise CompatibilityProfileError("profile must not enable vendor writes")
if safety.get("unknown_firmware_policy") != "reject-profile":
raise CompatibilityProfileError("unknown firmware must fail closed")
if safety.get("request_topic_subscription_enabled") is not False:
raise CompatibilityProfileError("request-topic subscription must remain disabled")
source_ids = _validate_sources(root)
_validate_source_references(root, source_ids)
_walk_and_validate_evidence(root)
_validate_transports(root)
_validate_channels(root)
_validate_acquisition_control(root)
return root
def load_compatibility_profile(path: Path | str = DEFAULT_PROFILE_PATH) -> dict[str, Any]:
"""Load a JSON profile, rejecting duplicate keys and unreviewed claims."""
resolved = Path(path).expanduser().resolve()
def reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
result: dict[str, Any] = {}
for key, value in pairs:
if key in result:
raise CompatibilityProfileError(f"duplicate JSON key {key!r}")
result[key] = value
return result
try:
raw = resolved.read_text(encoding="utf-8")
except OSError as exc:
raise CompatibilityProfileError(f"cannot read compatibility profile: {resolved}") from exc
try:
profile = json.loads(raw, object_pairs_hook=reject_duplicate_keys)
except json.JSONDecodeError as exc:
raise CompatibilityProfileError(f"invalid compatibility profile JSON: {exc}") from exc
return validate_compatibility_profile(profile)
def matches_target(
profile: dict[str, Any],
*,
firmware: str,
topology: str,
) -> bool:
"""Return whether an already validated exact-match profile covers the target."""
validated = validate_compatibility_profile(profile)
scope = validated["scope"]
return scope["firmware"]["version"] == firmware and scope["topology"] == topology
def _main() -> int:
parser = argparse.ArgumentParser(description="Validate an XGRIDS K1 compatibility profile")
parser.add_argument("path", nargs="?", type=Path, default=DEFAULT_PROFILE_PATH)
args = parser.parse_args()
profile = load_compatibility_profile(args.path)
print(profile["profile_id"])
return 0
if __name__ == "__main__":
raise SystemExit(_main())