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:
@@ -85,6 +85,148 @@ def cert(row):
|
||||
)
|
||||
|
||||
|
||||
def test_migrated_heartbeat_preserves_identity_and_old_reply_cannot_reverse(setup):
|
||||
fleet, _, _, _ = setup
|
||||
public, _ = create(setup)
|
||||
fleet.advance(public["id"])
|
||||
old = fleet.find(public["id"])
|
||||
payload = {
|
||||
**heartbeat(old),
|
||||
"core_endpoint": "https://100.64.20.5:8782",
|
||||
"endpoint_revision": 1,
|
||||
}
|
||||
assert fleet.receive(cert(old), "/v1/node/heartbeat", payload, address="192.168.20.5")[0] == 400
|
||||
assert fleet.find(public["id"])["binding"] == old["binding"]
|
||||
assert fleet.receive(cert(old), "/v1/node/heartbeat", payload, address="100.64.20.5")[0] == 200
|
||||
current = fleet.find(public["id"])
|
||||
assert current["core_address"] == "100.64.20.5"
|
||||
assert current["binding"]["binding_id"] == old["binding"]["binding_id"]
|
||||
assert current["binding"]["client_pem"] == old["binding"]["client_pem"]
|
||||
assert (
|
||||
fleet.receive(cert(old), "/v1/node/heartbeat", heartbeat(old), address="192.168.20.5")[0]
|
||||
== 409
|
||||
)
|
||||
assert fleet.find(public["id"])["binding"] == current["binding"]
|
||||
fleet.revoke(public["id"])
|
||||
assert fleet.receive(cert(old), "/v1/node/heartbeat", payload, address="100.64.20.5")[0] == 410
|
||||
|
||||
|
||||
def test_recovery_only_uses_previously_reported_tailnet_addresses():
|
||||
from k1link.fleet.recovery import node_addresses
|
||||
|
||||
assert node_addresses(
|
||||
{
|
||||
"inventory": {
|
||||
"networks": [
|
||||
{
|
||||
"up": True,
|
||||
"addresses": ["192.168.20.4/24", "8.8.8.8", "100.64.20.4/32", "::1"],
|
||||
},
|
||||
{"up": False, "addresses": ["100.64.1.1/32"]},
|
||||
]
|
||||
}
|
||||
}
|
||||
) == ["100.64.20.4"]
|
||||
|
||||
|
||||
def test_recovery_client_leaf_is_distinct_from_issuer_with_same_pinned_key(setup):
|
||||
from cryptography.x509.oid import ExtendedKeyUsageOID
|
||||
|
||||
from k1link.fleet.recovery import client_context
|
||||
|
||||
fleet, _, _, _ = setup
|
||||
client_context(fleet.trust)
|
||||
leaf, root = x509.load_pem_x509_certificates((fleet.root / "recovery-client.pem").read_bytes())
|
||||
assert leaf.subject != root.subject
|
||||
assert leaf.issuer == root.subject
|
||||
assert leaf.public_key().public_bytes_raw() == root.public_key().public_bytes_raw()
|
||||
assert public_id("core_", leaf.public_key()) == fleet.trust.core_id
|
||||
assert (
|
||||
ExtendedKeyUsageOID.CLIENT_AUTH
|
||||
in leaf.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value
|
||||
)
|
||||
leaf.verify_directly_issued_by(root)
|
||||
|
||||
|
||||
def test_recovery_pins_node_before_application_request(setup, monkeypatch):
|
||||
from k1link.fleet import recovery
|
||||
|
||||
fleet, _, _, _ = setup
|
||||
public, _ = create(setup)
|
||||
fleet.advance(public["id"])
|
||||
row = fleet.find(public["id"])
|
||||
row["inventory"] = {"networks": [{"up": True, "addresses": ["100.64.20.4/32"]}]}
|
||||
fleet.save(row)
|
||||
calls = []
|
||||
|
||||
class Connection:
|
||||
sock = None
|
||||
|
||||
def connect(self):
|
||||
self.sock = self
|
||||
|
||||
def getpeercert(self, **kwargs):
|
||||
return b"not the saved Node certificate"
|
||||
|
||||
def close(self):
|
||||
calls.append("closed")
|
||||
|
||||
monkeypatch.setattr(recovery.http.client, "HTTPSConnection", lambda *a, **kw: Connection())
|
||||
monkeypatch.setattr(recovery, "request", lambda *a: calls.append("request"))
|
||||
with pytest.raises(ValueError):
|
||||
recovery.recover(fleet, row, "100.64.20.4", None)
|
||||
assert calls == ["closed"]
|
||||
|
||||
|
||||
def test_recovery_rechecks_revocation_and_waits_for_heartbeat(setup, monkeypatch):
|
||||
from k1link.fleet import recovery
|
||||
|
||||
fleet, _, _, _ = setup
|
||||
public, _ = create(setup)
|
||||
fleet.advance(public["id"])
|
||||
row = fleet.find(public["id"])
|
||||
row["inventory"] = {"networks": [{"up": True, "addresses": ["100.64.20.4/32"]}]}
|
||||
fleet.save(row)
|
||||
calls = []
|
||||
|
||||
class Connection:
|
||||
def connect(self):
|
||||
self.sock = self
|
||||
|
||||
def getpeercert(self, **kwargs):
|
||||
return b"verified below by test adapter"
|
||||
|
||||
def getsockname(self):
|
||||
return ("100.64.20.5", 30000)
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
monkeypatch.setattr(recovery.http.client, "HTTPSConnection", lambda *a, **kw: Connection())
|
||||
monkeypatch.setattr(recovery, "verify_node_certificate", lambda *a: None)
|
||||
|
||||
def request(connection, path, body):
|
||||
calls.append(path)
|
||||
return {
|
||||
"schema": recovery.SCHEMA,
|
||||
"node_id": row["node_id"],
|
||||
"core_id": fleet.trust.core_id,
|
||||
"binding_id": row["binding"]["binding_id"],
|
||||
"endpoint": row["binding"]["endpoint"],
|
||||
"endpoint_revision": 0,
|
||||
}
|
||||
|
||||
monkeypatch.setattr(recovery, "request", request)
|
||||
recovery.recover(fleet, row, "100.64.20.4", None)
|
||||
assert calls == ["/v1/channel/inspect", "/v1/channel/migrate"]
|
||||
assert fleet.find(public["id"])["binding"] == row["binding"]
|
||||
assert fleet.public(fleet.find(public["id"]))["connectivity"] == "offline"
|
||||
calls.clear()
|
||||
fleet.revoke(public["id"])
|
||||
recovery.recover(fleet, row, "100.64.20.4", None)
|
||||
assert calls == ["/v1/channel/inspect"]
|
||||
|
||||
|
||||
def test_preview_add_is_durable_and_idempotent(setup):
|
||||
fleet, invite, _, calls = setup
|
||||
public, preview = create(setup)
|
||||
@@ -386,3 +528,53 @@ def test_sensor_cannot_claim_another_board_identity(setup):
|
||||
value["devices"][0]["snapshot"]["context"]["execution"]["node_id"] = "node_wrong"
|
||||
with pytest.raises(ValueError):
|
||||
validate_inventory(value, row["node_id"])
|
||||
|
||||
|
||||
def test_uninitialized_x4_can_be_prepared_remotely_with_instance_progress(setup):
|
||||
from k1link.fleet import sensors
|
||||
|
||||
public, _ = create(setup)
|
||||
fleet = setup[0]
|
||||
row = fleet.find(public["id"])
|
||||
inventory = sensor_inventory(row)
|
||||
item = inventory["devices"][0]
|
||||
item.update(id="instax4_" + "a" * 32, kind="insta360.x4", prepared=False, configured=False)
|
||||
item["snapshot"]["context"]["device"].update(
|
||||
device_id=item["id"],
|
||||
model={
|
||||
"plugin_id": "missioncore.insta360",
|
||||
"plugin_version": "0.1.0",
|
||||
"model_id": "insta360.x4",
|
||||
},
|
||||
)
|
||||
assert fleet.receive(cert(row), "/v1/node/heartbeat", inventory)[0] == 200
|
||||
now = datetime.now(UTC)
|
||||
command = {
|
||||
"api_version": "missioncore.nodedc/plugin-sdk/v0alpha2",
|
||||
"kind": "OperationRequest",
|
||||
"operation_id": "op_" + "b" * 32,
|
||||
"idempotency_key": "op_" + "b" * 32,
|
||||
"session": {"device_id": item["id"], "session_id": "sensor_test"},
|
||||
"requested_at": now.isoformat(),
|
||||
"deadline_at": (now + timedelta(seconds=350)).isoformat(),
|
||||
"action_id": "prepare",
|
||||
"parameters": {},
|
||||
}
|
||||
assert sensors.submit(fleet, row["id"], command)["state"] == "queued"
|
||||
_, result = fleet.receive(cert(row), "/v1/node/heartbeat", inventory)
|
||||
assert result["sensor_commands"] == [command]
|
||||
progress = {
|
||||
"operation_id": command["operation_id"],
|
||||
"device_id": item["id"],
|
||||
"model_id": "insta360.x4",
|
||||
"phase": "verify",
|
||||
"state": "running",
|
||||
"steps": [{"id": "profile", "state": "complete"}, {"id": "verify", "state": "running"}],
|
||||
}
|
||||
inventory["sensor_state"]["preparations"] = [progress]
|
||||
inventory["sensor_results"] = [
|
||||
{"command": command, "state": "running", "preparation": progress}
|
||||
]
|
||||
assert fleet.receive(cert(row), "/v1/node/heartbeat", inventory)[0] == 200
|
||||
assert sensors.operation(fleet, row["id"], command["operation_id"])["preparation"] == progress
|
||||
assert fleet.find(row["id"])["sensor_state"]["preparations"] == [progress]
|
||||
|
||||
Reference in New Issue
Block a user