Add packaged Insta360 X4 integration and recover paired Node channels

Discover independent camera instances and prepare their versioned runtime
from Node or remote Core. Add isolated SDK workers, camera controls, raw
dual-fisheye WebRTC preview, and shared action/region loading states.

Recover existing Node bindings over known Tailscale addresses after a Core
LAN address change. Preserve identities and trust, pin both peers, migrate
endpoints with revision checks, and require real heartbeats for online status.
Fix the Python client certificate profile for Go X509 verification.

Pin Design Guideline 8c53f73 and retain installer/build/acceptance history.
Node 0.8.19 is installed; X4 0.1.3-3 is bundled but hardware activation is pending.

Validation: qualified DG/Node builds and Go race tests; 31 fleet tests;
Python-to-Go certificate interoperability and live tailnet recovery with five
fresh heartbeats; prior 38 X4 tests and bounded remote WebRTC acceptance.
Clean-OS, replug/power autonomy, local X4 video and long-run stability remain open.
This commit is contained in:
DCCONSTRUCTIONS
2026-09-10 09:21:24 +03:00
parent 54a85fdf50
commit a3c15e11e9
125 changed files with 11916 additions and 251 deletions
+270
View File
@@ -0,0 +1,270 @@
"""Unprivileged model broker; public authority stays in Node/Core."""
import pwd
import re
import threading
import uuid
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from .errors import failure
from .http import Server, request
from .operations import UNKNOWN, Operations
ROOT = Path("/run/mission-core-insta360")
INSTANCES = Path("/run/mission-core-x4-instances")
IDENTIFIER = re.compile(r"instax4_[0-9a-f]{32}")
STATE = Path("/var/lib/mission-core-insta360-broker")
def worker_socket(identifier):
if not isinstance(identifier, str) or not IDENTIFIER.fullmatch(identifier):
raise ValueError("Invalid camera identity")
return INSTANCES / identifier / "driver.sock"
class Broker:
def __init__(self):
self.instance = "x4broker_" + uuid.uuid4().hex
self.media_lock = threading.Lock()
self.media = None
self.media_operations = {}
self.capture_locks = {}
def capture_lock(self, identifier):
with self.media_lock:
if identifier not in self.capture_locks and len(self.capture_locks) >= 500:
raise RuntimeError("Camera inventory exceeds the protocol limit")
return self.capture_locks.setdefault(identifier, threading.Lock())
def capture_operation(self, identifier, value):
path = worker_socket(identifier)
lock = self.capture_lock(identifier)
with self.media_lock:
if self.media is None:
from .media import Engine
self.media = Engine()
# Only one camera's START/STOP is serialized here; other devices keep
# independent locks and SDK operation journals.
with lock:
current = request(path, "/snapshot", timeout=2)
session = current["session_id"]
if current.get("id") != identifier or value.get("session") != {
"device_id": identifier,
"session_id": session,
}:
raise ValueError("Camera session changed")
key = (identifier, session)
starting = value["action_id"] == "preview.start"
starting_idle = starting and current.get("status", {}).get("preview") != 1
if starting:
if not current.get("online"):
raise RuntimeError("Camera state is unavailable")
self.media.capture(key, path, True)
try:
result = request(path, "/operation", value, timeout=55)
except (OSError, RuntimeError, ValueError):
if starting_idle:
self.media.capture(key, path, False)
raise
if (starting_idle and result.get("state") != "complete") or (
not starting and result.get("state") == "complete"
):
# Releasing a decoder never sends STOP to the camera or SD.
self.media.capture(key, path, False)
return result
def verify_operation(self, identifier, value):
path = worker_socket(identifier)
# Serialize with preview transitions, preserving one receipt namespace
# whether verification uses the live decoder or an idle camera check.
with self.capture_lock(identifier):
current = request(path, "/snapshot", timeout=2)
if current.get("id") != identifier:
raise ValueError("Camera identity changed")
operation = Operations(
identifier,
current["session_id"],
STATE / identifier / "verification",
VerificationCalls(self, path, value),
)
return operation.execute(value)
def media_operation(self, identifier, value):
path = worker_socket(identifier)
current = request(path, "/snapshot", timeout=2)
if current.get("id") != identifier:
raise ValueError("Camera identity changed")
session = current["session_id"]
with self.media_lock:
if self.media is None:
from .media import Engine
self.media = Engine()
previous = self.media_operations.get(identifier)
if previous is None or previous.session_id != session:
previous = Operations(
identifier,
session,
STATE / identifier,
MediaCalls(self.media, (identifier, session), path),
)
self.media_operations[identifier] = previous
return previous.execute(value)
def snapshots(self):
paths = sorted(INSTANCES.glob("instax4_*/driver.sock"))
if len(paths) > 500:
raise RuntimeError("Camera inventory exceeds the protocol limit")
def read(path):
if not IDENTIFIER.fullmatch(path.parent.name):
return None
try:
value = request(path, "/snapshot", timeout=0.75)
return value if value.get("id") == path.parent.name else None
except (OSError, RuntimeError, ValueError):
return None
pool = ThreadPoolExecutor(max_workers=8)
pending = [pool.submit(read, path) for path in paths]
values = []
try:
for future in as_completed(pending, timeout=2):
value = future.result()
if value is not None:
values.append(value)
except TimeoutError:
pass
finally:
pool.shutdown(wait=False, cancel_futures=True)
return sorted(values, key=lambda value: value["id"])
def item(self, value, node):
status = value["status"]
preview = status.get("preview")
return {
"id": value["id"],
"name": "Insta360 X4",
"model": "Insta360 X4",
"kind": "insta360.x4",
"initializable": True,
"configured": False,
"prepared": value["prepared"],
"verified": value["verified"],
"online": value["online"],
"preparation_safe": value["preparation_safe"],
"usb": "USB",
"firmware": status.get("firmware"),
"layers": [],
"camera_status": status,
"snapshot": {
"revision": value["revision"],
"observed_at": value["observed_at"],
"context": {
"session_id": value["session_id"],
"opened_at": value["opened_at"],
"device": {
"device_id": value["id"],
"stability": "stable",
"basis": "hardware-identifier",
"model": {
"plugin_id": "missioncore.insta360",
"plugin_version": "0.1.3",
"model_id": "insta360.x4",
},
},
"execution": {
"node_id": node,
"agent_instance_id": self.instance,
"platform": "linux",
},
},
"acquisition": "streaming"
if preview == 1
else "idle"
if preview == 0
else "failed",
"connectivity": "connected" if value["online"] else "offline",
"enrollment": "enrolled" if value["prepared"] else "empty",
"message": value["message"],
},
}
def dispatch(self, method, route, value, headers):
if method == "GET" and route == "/prepare-safe":
snapshots = self.snapshots()
expected = list(INSTANCES.glob("instax4_*"))
return {
"safe": len(snapshots) == len(expected)
and all(item["preparation_safe"] for item in snapshots)
}
node = headers.get("X-Node-Id", "")
if not re.fullmatch(r"node_[0-9a-f]{64}", node):
raise ValueError("Node identity is required")
if method == "GET" and route == "/inventory":
return {"items": [self.item(item, node) for item in self.snapshots()]}
if method == "POST" and route == "/operation":
identifier = value["session"]["device_id"]
if value.get("action_id") in ("preview.start", "preview.stop"):
return self.capture_operation(identifier, value)
if value.get("action_id") in ("offer", "close-peer"):
return self.media_operation(identifier, value)
if value.get("action_id") == "verify":
return self.verify_operation(identifier, value)
return request(worker_socket(identifier), "/operation", value, timeout=55)
raise ValueError("Unsupported model route")
class MediaCalls:
def __init__(self, engine, key, path):
self.engine, self.key, self.path = engine, key, path
def call(self, action, params):
if action == "offer":
current = request(self.path, "/snapshot", timeout=2)
if (
current.get("session_id") != self.key[1]
or not current.get("online")
or current.get("status", {}).get("preview") != 1
):
raise RuntimeError("Camera preview is not active")
return self.engine.call(self.key, self.path, action, params)
class VerificationCalls:
def __init__(self, broker, path, command):
self.broker, self.path, self.command = broker, path, command
def call(self, action, params):
current = request(self.path, "/snapshot", timeout=2)
session = self.command["session"]
if (
current.get("id") != session["device_id"]
or current.get("session_id") != session["session_id"]
or not current.get("online")
):
return dict(UNKNOWN)
status = current.get("status", {})
if status.get("recording") != 0:
return failure("verification_recording_active")
if status.get("preview") == 1:
engine = self.broker.media
if engine is None:
return failure("preview_restart_required")
return engine.verify((session["device_id"], session["session_id"]))
if status.get("preview") == 0:
# Preserve worker ownership of the bounded temporary idle preview.
return request(self.path, "/operation", self.command, timeout=55)
return dict(UNKNOWN)
def main():
path = ROOT / "driver.sock"
if path.exists():
path.unlink()
allowed = {0, pwd.getpwnam("mission-core-node").pw_uid}
with Server(path, Broker().dispatch, allowed) as server:
path.chmod(0o660)
server.serve_forever(poll_interval=0.5)