remoteRF-server 0.1.98__tar.gz → 0.1.99__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/PKG-INFO +1 -1
  2. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/pyproject.toml +1 -1
  3. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/host/host_tunnel_server.py +60 -18
  4. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/device_manager.py +0 -1
  5. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/PKG-INFO +1 -1
  6. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/README.md +0 -0
  7. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/setup.cfg +0 -0
  8. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/__init__.py +0 -0
  9. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/__init__.py +0 -0
  10. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  11. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  12. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  13. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  14. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  15. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/__init__.py +0 -0
  16. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  17. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/api_token.py +0 -0
  18. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  19. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/db_location.py +0 -0
  20. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/list_string.py +0 -0
  21. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  22. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/drivers/__init__.py +0 -0
  23. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  24. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  25. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/host/__init__.py +0 -0
  26. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/host/host_directory_store.py +0 -0
  27. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/__init__.py +0 -0
  28. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/acc_perms.py +0 -0
  29. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/cert_provider.py +0 -0
  30. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/grpc_server.py +0 -0
  31. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/reservation.py +0 -0
  32. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/rpc_manager.py +0 -0
  33. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/user_group_cli.py +0 -0
  34. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/server/user_group_handler.py +0 -0
  35. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/serverrf_cli.py +0 -0
  36. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/tools/__init__.py +0 -0
  37. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server/tools/gen_certs.py +0 -0
  38. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
  39. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  40. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  41. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/requires.txt +0 -0
  42. {remoterf_server-0.1.98 → remoterf_server-0.1.99}/src/remoteRF_server.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.98
3
+ Version: 0.1.99
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "0.1.98"
7
+ version = "0.1.99"
8
8
  dependencies = [
9
9
  "grpcio",
10
10
  "protobuf",
@@ -263,6 +263,7 @@ class HostTunnelRegistry:
263
263
 
264
264
  Enforces:
265
265
  - device_id is globally unique across hosts (rejects conflicting host announce)
266
+ - STRICT MODE: device_id MUST be a canonical decimal gid string ("10", not "010", not "pluto:...")
266
267
  """
267
268
 
268
269
  def __init__(self, *, active_cache_ttl_ms: int = 250, dir_cache_ttl_ms: int = 1000) -> None:
@@ -346,8 +347,15 @@ class HostTunnelRegistry:
346
347
  all_devices: List[str] = sorted(set(known_devices) | devices_from_host_lists)
347
348
 
348
349
  for device_id in all_devices:
350
+ device_id = (device_id or "").strip()
349
351
  if not device_id:
350
352
  continue
353
+
354
+ # STRICT MODE: ignore any persisted non-numeric / non-canonical IDs
355
+ if not device_id.isdigit() or device_id != str(int(device_id)):
356
+ warn(f"[registry] skipping persisted non-numeric/non-canonical device_id={device_id!r}")
357
+ continue
358
+
351
359
  dk = sanitize_env_key(device_id)
352
360
 
353
361
  host_id = (kv_meta.get(f"DEVICE_{dk}_HOST", "") or "").strip()
@@ -561,12 +569,40 @@ class HostTunnelRegistry:
561
569
  def announce_devices(self, host_id: str, devices: list) -> None:
562
570
  info(f"[registry] announce_devices: host_id={host_id!r} n={len(devices)}")
563
571
 
572
+ # STRICT MODE:
573
+ # - device_id MUST be canonical decimal string for gid (e.g. "10")
574
+ # - reject "010", reject "pluto:...", reject duplicates in a single announce
575
+ pairs: List[Tuple[Any, str]] = []
576
+ seen: Set[str] = set()
577
+
578
+ for d in devices:
579
+ raw = str(getattr(d, "device_id", "") or "").strip()
580
+ if not raw:
581
+ continue
582
+
583
+ if not raw.isdigit():
584
+ raise ValueError(
585
+ f"Host device_id must be a numeric gid string (e.g. '10'). "
586
+ f"Got {raw!r} from host_id={host_id!r}."
587
+ )
588
+
589
+ canon = str(int(raw))
590
+ if raw != canon:
591
+ raise ValueError(
592
+ f"Host device_id must be canonical decimal with no leading zeros. "
593
+ f"Got {raw!r}; use {canon!r} (host_id={host_id!r})."
594
+ )
595
+
596
+ if canon in seen:
597
+ raise ValueError(
598
+ f"Duplicate device_id {canon!r} in device announce from host_id={host_id!r}."
599
+ )
600
+ seen.add(canon)
601
+ pairs.append((d, canon))
602
+
564
603
  # conflict check first (no partial apply)
565
604
  with self._lock:
566
- for d in devices:
567
- device_id = str(getattr(d, "device_id", "")).strip()
568
- if not device_id:
569
- continue
605
+ for _, device_id in pairs:
570
606
  existing = self._routes.get(device_id)
571
607
  if existing is not None and existing.host_id != host_id:
572
608
  warn(
@@ -583,12 +619,13 @@ class HostTunnelRegistry:
583
619
  with self._lock:
584
620
  self._host_devices.setdefault(host_id, set())
585
621
 
622
+ # Preserve old behavior: skip empty ids with warn (but strict checks already handled non-empty bad ones)
586
623
  for d in devices:
587
- device_id = str(getattr(d, "device_id", "")).strip()
588
- if not device_id:
624
+ device_id0 = str(getattr(d, "device_id", "") or "").strip()
625
+ if not device_id0:
589
626
  warn(f"[registry] announce_devices: skipping device with empty device_id host_id={host_id!r}")
590
- continue
591
627
 
628
+ for d, device_id in pairs:
592
629
  try:
593
630
  local_id = int(getattr(d, "local_id", 0))
594
631
  except Exception:
@@ -620,10 +657,12 @@ class HostTunnelRegistry:
620
657
  try:
621
658
  tmp = host_tunnel_pb2.DeviceInfo()
622
659
  tmp.CopyFrom(d)
660
+ # ensure stored info reflects canonical device_id key
661
+ tmp.device_id = str(device_id)
623
662
  self._device_infos[device_id] = tmp
624
663
  except Exception:
625
664
  self._device_infos[device_id] = host_tunnel_pb2.DeviceInfo(
626
- device_id=device_id,
665
+ device_id=str(device_id),
627
666
  local_id=int(local_id),
628
667
  label=label,
629
668
  serial=serial,
@@ -1077,6 +1116,12 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
1077
1116
  try:
1078
1117
  self.registry.announce_devices(host_id, list(ann.devices))
1079
1118
  info(f"[inbound] registry announce applied host_id={host_id!r} stream_sess={stream_uuid}")
1119
+ except ValueError as e:
1120
+ error(f"[inbound] INVALID_ARGUMENT device announce host_id={host_id!r} stream_sess={stream_uuid} err={e}")
1121
+ fatal["code"] = "INVALID_ARGUMENT"
1122
+ fatal["details"] = str(e)
1123
+ stop.set()
1124
+ return
1080
1125
  except DeviceIdConflictError as e:
1081
1126
  error(f"[inbound] DeviceIdConflictError host_id={host_id!r} stream_sess={stream_uuid} err={e}")
1082
1127
  fatal["code"] = "ALREADY_EXISTS"
@@ -1123,7 +1168,7 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
1123
1168
  err_s = str(getattr(mr, "error", "") or "")
1124
1169
  info(f"[inbound] META_RESPONSE ok={ok} error={err_s!r} stream_sess={stream_uuid}")
1125
1170
 
1126
- # ---- NEW: apply devices from meta_response if present ----
1171
+ # ---- apply devices from meta_response if present ----
1127
1172
  if ok and host_id is not None:
1128
1173
  devs = []
1129
1174
  # try common field names defensively
@@ -1142,6 +1187,12 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
1142
1187
  self.registry.announce_devices(host_id, devs)
1143
1188
  # optional: heartbeat bump
1144
1189
  self.registry.heartbeat(host_id, now_ms())
1190
+ except ValueError as e:
1191
+ error(f"[inbound] META_RESPONSE INVALID_ARGUMENT host_id={host_id!r} err={e}")
1192
+ fatal["code"] = "INVALID_ARGUMENT"
1193
+ fatal["details"] = str(e)
1194
+ stop.set()
1195
+ return
1145
1196
  except Exception:
1146
1197
  exception("[inbound] META_RESPONSE announce_devices failed")
1147
1198
  else:
@@ -1330,15 +1381,6 @@ _TUNNEL_REGISTRY_LOCK = threading.Lock()
1330
1381
  TUNNEL_REGISTRY: Optional["HostTunnelRegistry"] = None
1331
1382
 
1332
1383
  def get_tunnel_registry(*, create: bool = True) -> Optional["HostTunnelRegistry"]:
1333
- """
1334
- Returns the process-wide HostTunnelRegistry instance.
1335
-
1336
- IMPORTANT: Import the MODULE (e.g. `import ... as hts`) and access
1337
- `hts.get_tunnel_registry()` or `hts.TUNNEL_REGISTRY`.
1338
-
1339
- Avoid `from host_tunnel_server import TUNNEL_REGISTRY` if you ever plan
1340
- to replace it, because that snapshots the value at import time.
1341
- """
1342
1384
  global TUNNEL_REGISTRY
1343
1385
 
1344
1386
  if TUNNEL_REGISTRY is not None:
@@ -266,7 +266,6 @@ def _host_devices_str_snapshot() -> Dict[int, str]:
266
266
 
267
267
  return out
268
268
 
269
-
270
269
  # ============================================================
271
270
  # Config paths (server-side)
272
271
  # ============================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.98
3
+ Version: 0.1.99
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf