remoteRF-server 1.0.3__tar.gz → 1.0.5__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 (74) hide show
  1. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/PKG-INFO +5 -31
  2. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/README.md +4 -30
  3. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/pyproject.toml +1 -1
  4. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/schema.py +2 -2
  5. remoterf_server-1.0.5/src/remoteRF_server/drivers/usrp/policy.py +41 -0
  6. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +8 -25
  7. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/usrp_schema.py +1 -2
  8. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/device_manager.py +6 -1
  9. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/dynamic_v2.py +4 -34
  10. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/grpc_server.py +13 -5
  11. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/serverrf_cli.py +14 -18
  12. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/PKG-INFO +5 -31
  13. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_cert_provider.py +24 -0
  14. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_dynamic_v2.py +108 -79
  15. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_pluto_compat.py +15 -0
  16. remoterf_server-1.0.3/src/remoteRF_server/drivers/usrp/policy.py +0 -363
  17. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/setup.cfg +0 -0
  18. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/__init__.py +0 -0
  19. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/__init__.py +0 -0
  20. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  21. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  22. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  23. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  24. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  25. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
  26. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/__init__.py +0 -0
  27. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/device_schema.py +0 -0
  28. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
  29. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
  30. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/v2.py +0 -0
  31. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/__init__.py +0 -0
  32. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  33. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/api_token.py +0 -0
  34. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  35. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/db_location.py +0 -0
  36. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/list_string.py +0 -0
  37. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  38. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/__init__.py +0 -0
  39. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  40. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
  41. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  42. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
  43. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
  44. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/__init__.py +0 -0
  45. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_auth_token.py +0 -0
  46. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_directory_store.py +0 -0
  47. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
  48. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/__init__.py +0 -0
  49. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/acc_perms.py +0 -0
  50. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/cert_provider.py +0 -0
  51. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/reservation.py +0 -0
  52. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/rpc_manager.py +0 -0
  53. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/user_group_cli.py +0 -0
  54. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/user_group_handler.py +0 -0
  55. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/v2_errors.py +0 -0
  56. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/__init__.py +0 -0
  57. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gen_certs.py +0 -0
  58. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gist_status.py +0 -0
  59. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
  60. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
  61. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
  62. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  63. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  64. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/requires.txt +0 -0
  65. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/top_level.txt +0 -0
  66. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_default_usrp_schema.py +0 -0
  67. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_idl_schema.py +0 -0
  68. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_process_arg.py +0 -0
  69. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_reservation_timestamps.py +0 -0
  70. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_rpc_manager_calln.py +0 -0
  71. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_client_server_hardware.py +0 -0
  72. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_client_server_localhost.py +0 -0
  73. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
  74. {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_uhd410_hardware.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -329,36 +329,10 @@ devices:
329
329
  serial: 31A2B3C
330
330
  ```
331
331
 
332
- Transmit is denied if `~/.config/remoterf/usrp_policy.yml` is missing,
333
- unreadable, invalid, or does not explicitly enable and bound the requested
334
- operation. Low-level writes are also denied unless both globally enabled and
335
- method/path/address allowlisted. A starting policy looks like:
336
-
337
- ```yaml
338
- defaults:
339
- tx_enabled: false
340
- low_level_write_enabled: false
341
-
342
- devices:
343
- "31A2B3C":
344
- tx_enabled: true
345
- tx_frequency_ranges_hz:
346
- - [902000000, 928000000]
347
- max_tx_gain_db: 20
348
- max_tx_normalized_gain: 0.5
349
- max_tx_power_dbm: -10
350
- max_tx_bandwidth_hz: 2000000
351
- max_tx_sample_rate_sps: 2000000
352
- allowed_tx_antennas: [TX/RX]
353
- max_burst_seconds: 1.0
354
- max_duty_cycle: 0.1
355
- duty_cycle_window_seconds: 60
356
- low_level_write_enabled: false
357
- low_level_write_methods: []
358
- gpio_write_masks: {}
359
- property_write_paths: []
360
- register_write_addresses: []
361
- ```
332
+ All USRP operations exposed by the Dynamic v2 schema are available without a
333
+ separate policy file. This includes transmit controls, transmit streaming,
334
+ GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
335
+ or required by the current server.
362
336
 
363
337
  On every qualified target, generate the introspection record and enforce the
364
338
  pinned API gate:
@@ -314,36 +314,10 @@ devices:
314
314
  serial: 31A2B3C
315
315
  ```
316
316
 
317
- Transmit is denied if `~/.config/remoterf/usrp_policy.yml` is missing,
318
- unreadable, invalid, or does not explicitly enable and bound the requested
319
- operation. Low-level writes are also denied unless both globally enabled and
320
- method/path/address allowlisted. A starting policy looks like:
321
-
322
- ```yaml
323
- defaults:
324
- tx_enabled: false
325
- low_level_write_enabled: false
326
-
327
- devices:
328
- "31A2B3C":
329
- tx_enabled: true
330
- tx_frequency_ranges_hz:
331
- - [902000000, 928000000]
332
- max_tx_gain_db: 20
333
- max_tx_normalized_gain: 0.5
334
- max_tx_power_dbm: -10
335
- max_tx_bandwidth_hz: 2000000
336
- max_tx_sample_rate_sps: 2000000
337
- allowed_tx_antennas: [TX/RX]
338
- max_burst_seconds: 1.0
339
- max_duty_cycle: 0.1
340
- duty_cycle_window_seconds: 60
341
- low_level_write_enabled: false
342
- low_level_write_methods: []
343
- gpio_write_masks: {}
344
- property_write_paths: []
345
- register_write_addresses: []
346
- ```
317
+ All USRP operations exposed by the Dynamic v2 schema are available without a
318
+ separate policy file. This includes transmit controls, transmit streaming,
319
+ GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
320
+ or required by the current server.
347
321
 
348
322
  On every qualified target, generate the introspection record and enforce the
349
323
  pinned API gate:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "1.0.3"
7
+ version = "1.0.5"
8
8
  description = "RemoteRF server-side control package"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -6,7 +6,7 @@ Admin writes one .py per device TYPE, drops it in ~/.config/remoterf/drivers/,
6
6
  and the server auto-discovers it on startup via load_drivers().
7
7
 
8
8
  ~/.config/remoterf/
9
- devices.env ← "I have 3 plutos at these serials"
9
+ devices.yml ← "I have 3 devices at these serials"
10
10
  drivers/
11
11
  pluto_schema.py ← "here's what a pluto IS"
12
12
  hackrf_schema.py
@@ -531,7 +531,7 @@ class DeviceSchema:
531
531
  Base for all device schemas. Admin subclasses this once per device type.
532
532
 
533
533
  Class attributes (admin sets these):
534
- device_type str — must match the TYPE in devices.env
534
+ device_type str — must match device_type in devices.yml
535
535
  driver_version str — semver
536
536
 
537
537
  Admin implements:
@@ -0,0 +1,41 @@
1
+ """Compatibility shim for unrestricted USRP access.
2
+
3
+ USRP policy enforcement is currently disabled. ``USRPPolicy`` remains importable
4
+ so older integrations do not fail at import time, but every supported control,
5
+ streaming, property-tree, GPIO, and register operation passes through without
6
+ policy checks.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import Any, Mapping
12
+
13
+
14
+ class USRPPolicy:
15
+ """No-op policy retained for protocol and integration compatibility."""
16
+
17
+ def __init__(self, config: Mapping[str, Any] | None = None, *, source: str = ""):
18
+ self.config = dict(config or {})
19
+ self.source = source
20
+
21
+ @classmethod
22
+ def load(cls, *, gid: int, serial: str = "", path: Path | None = None) -> "USRPPolicy":
23
+ del gid, serial, path
24
+ return cls()
25
+
26
+ @property
27
+ def tx_enabled(self) -> bool:
28
+ return True
29
+
30
+ @property
31
+ def restrictions(self) -> dict[str, Any]:
32
+ return {}
33
+
34
+ def check_control(self, method: str, args: Mapping[str, Any]) -> None:
35
+ del method, args
36
+
37
+ def check_tx_duration(self, duration: float) -> None:
38
+ del duration
39
+
40
+ def check_handle_call(self, type_id: str, method: str, args: Mapping[str, Any]) -> None:
41
+ del type_id, method, args
@@ -135,11 +135,6 @@ HANDLE_METHOD_RETURN_TYPES = {
135
135
  ("uhd.property.dboard_iface", "get_desired"): "uhd.usrp.dboard_iface",
136
136
  }
137
137
 
138
- LOW_LEVEL_WRITES = {
139
- "set_gpio_attr", "set_gpio_src", "set_user_register",
140
- "set_rx_filter", "set_tx_filter",
141
- }
142
-
143
138
  USRP2901_UNSUPPORTED_HANDLE_TYPES = {
144
139
  "uhd.usrp.radio_control",
145
140
  "uhd.usrp.mboard_controller",
@@ -1305,7 +1300,7 @@ def _objects() -> list[dict]:
1305
1300
  "uhd.property_tree",
1306
1301
  kind="remote_handle",
1307
1302
  mutability="remote_handle",
1308
- ownership={"scope": "session", "default_access": "read_only"},
1303
+ ownership={"scope": "session", "default_access": "read_write"},
1309
1304
  ),
1310
1305
  *[
1311
1306
  object_type(
@@ -1315,7 +1310,7 @@ def _objects() -> list[dict]:
1315
1310
  ownership={
1316
1311
  "scope": "session",
1317
1312
  "parent": "uhd.property_tree",
1318
- "default_access": "read_only",
1313
+ "default_access": "read_write",
1319
1314
  },
1320
1315
  )
1321
1316
  for suffix in (
@@ -1331,25 +1326,25 @@ def _objects() -> list[dict]:
1331
1326
  "uhd.usrp.radio_control",
1332
1327
  kind="remote_handle",
1333
1328
  mutability="remote_handle",
1334
- ownership={"scope": "session", "default_access": "read_only"},
1329
+ ownership={"scope": "session", "default_access": "read_write"},
1335
1330
  ),
1336
1331
  object_type(
1337
1332
  "uhd.usrp.mboard_controller",
1338
1333
  kind="remote_handle",
1339
1334
  mutability="remote_handle",
1340
- ownership={"scope": "session", "default_access": "read_only"},
1335
+ ownership={"scope": "session", "default_access": "read_write"},
1341
1336
  ),
1342
1337
  object_type(
1343
1338
  "uhd.usrp.dboard_iface",
1344
1339
  kind="remote_handle",
1345
1340
  mutability="remote_handle",
1346
- ownership={"scope": "session", "default_access": "read_only"},
1341
+ ownership={"scope": "session", "default_access": "read_write"},
1347
1342
  ),
1348
1343
  object_type(
1349
1344
  "uhd.usrp.user_settings_iface",
1350
1345
  kind="remote_handle",
1351
1346
  mutability="remote_handle",
1352
- ownership={"scope": "session", "default_access": "read_only"},
1347
+ ownership={"scope": "session", "default_access": "read_write"},
1353
1348
  ),
1354
1349
  ]
1355
1350
 
@@ -1415,7 +1410,6 @@ def _streamer_methods() -> list[dict]:
1415
1410
  )
1416
1411
  ],
1417
1412
  execution="sample_stream",
1418
- policy="tx_samples",
1419
1413
  ),
1420
1414
  method(
1421
1415
  "recv_async_msg", "uhd.usrp.TXStreamer",
@@ -1514,7 +1508,6 @@ def _property_tree_methods() -> list[dict]:
1514
1508
  )
1515
1509
  ],
1516
1510
  execution="server_handle",
1517
- policy="low_level_write",
1518
1511
  )
1519
1512
  )
1520
1513
  return methods
@@ -1531,13 +1524,13 @@ CAPABILITY_FIELDS = (
1531
1524
  "tx_bandwidth_ranges_per_channel", "master_clock_rate_range", "time_sources",
1532
1525
  "clock_sources", "gpio_banks", "sensors", "supported_cpu_formats",
1533
1526
  "supported_otw_formats", "maximum_remote_payload_bytes",
1534
- "supported_streaming_protocol_versions", "policy_restrictions",
1527
+ "supported_streaming_protocol_versions",
1535
1528
  "remote_resource_limits", "advanced_interfaces",
1536
1529
  )
1537
1530
 
1538
1531
  ERRORS = (
1539
1532
  "RemoteRFTransportError", "RemoteRFSessionExpiredError",
1540
- "RemoteRFReservationError", "RemoteRFPolicyError",
1533
+ "RemoteRFReservationError",
1541
1534
  "RemoteRFStaleHandleError", "RemoteRFProtocolError",
1542
1535
  "RemoteRFOverloadError",
1543
1536
  "RemoteRFServerOverloadedError", "RemoteRFNativeUHDError",
@@ -1558,15 +1551,6 @@ def build_usrp_schema_v2(driver_version: str = "0.0.1") -> dict:
1558
1551
  coverage: dict[str, str] = {}
1559
1552
  for name in MULTIUSRP_METHODS:
1560
1553
  execution = "server_optimized" if name in {"recv_num_samps", "send_waveform"} else "server_control"
1561
- policy = None
1562
- if (
1563
- name.startswith("set_tx_")
1564
- or name == "set_normalized_tx_gain"
1565
- or name == "send_waveform"
1566
- ):
1567
- policy = "tx_control"
1568
- elif name in LOW_LEVEL_WRITES:
1569
- policy = "low_level_write"
1570
1554
  availability = {"runtime_member": name}
1571
1555
  if name in OPTIONAL_METHODS:
1572
1556
  availability["optional"] = True
@@ -1577,7 +1561,6 @@ def build_usrp_schema_v2(driver_version: str = "0.0.1") -> dict:
1577
1561
  signatures.get(name, _fallback_overload()),
1578
1562
  execution=execution,
1579
1563
  availability=availability,
1580
- policy=policy,
1581
1564
  )
1582
1565
  )
1583
1566
  coverage[f"uhd.usrp.MultiUSRP.{name}"] = (
@@ -518,7 +518,7 @@ class UsrpSchema(DeviceSchema):
518
518
  return str(value)
519
519
  return ""
520
520
 
521
- def get_capabilities_v2(self, *, policy_restrictions=None):
521
+ def get_capabilities_v2(self):
522
522
  device = self.device
523
523
  if device is None:
524
524
  raise RuntimeError("USRP session is not open")
@@ -615,7 +615,6 @@ class UsrpSchema(DeviceSchema):
615
615
  },
616
616
  "maximum_remote_payload_bytes": 4 * 1024 * 1024,
617
617
  "supported_streaming_protocol_versions": ["1.0"],
618
- "policy_restrictions": dict(policy_restrictions or {}),
619
618
  }
620
619
 
621
620
  def _streamers(self):
@@ -314,6 +314,11 @@ def _load_device_records() -> Dict[int, Dict[str, str]]:
314
314
  def _connect_from_record(rec: Dict[str, Any]):
315
315
  """Returns (schema_instance_or_None, ident, dtype)."""
316
316
  dtype = (rec.get("TYPE") or "").strip().lower()
317
+ # Older serverrf builds serialized the --pluto shorthand as "pluto",
318
+ # while the packaged schema has always registered as "adalm_pluto".
319
+ # Keep those inventories usable while all new writes use the canonical type.
320
+ if dtype == "pluto":
321
+ dtype = "adalm_pluto"
317
322
  ident = (rec.get("IDENT") or "").strip()
318
323
  init_kwargs = rec.get("INIT") or {}
319
324
 
@@ -873,7 +878,7 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
873
878
 
874
879
  def reload_devices() -> None:
875
880
  """
876
- Reload devices.env and reconnect local devices.
881
+ Reload devices.yml and reconnect local devices.
877
882
  Preserves existing salt/hash for devices that remain (same gid),
878
883
  and preserves host virtual devices + their auth state.
879
884
  """
@@ -23,6 +23,8 @@ from ..common.idl.v2 import (
23
23
  SCHEMA_VERSION,
24
24
  STREAMING_PROTOCOL_VERSION,
25
25
  )
26
+ # Retained as a module attribute for integrations that patched the former
27
+ # policy loader. Runtime access is no longer gated by it.
26
28
  from ..drivers.usrp.policy import USRPPolicy
27
29
  from ..drivers.usrp.uhd_manifest import (
28
30
  HANDLE_METHOD_RETURN_TYPES,
@@ -39,7 +41,6 @@ from .device_manager import (
39
41
  from .v2_errors import (
40
42
  RemoteRFNativeUHDError,
41
43
  RemoteRFOverloadError,
42
- RemoteRFPolicyError,
43
44
  RemoteRFProtocolError,
44
45
  RemoteRFReservationError,
45
46
  RemoteRFServerOverloadedError,
@@ -466,7 +467,6 @@ class DeviceSession:
466
467
  gid: int
467
468
  schema: Any
468
469
  schema_v2: dict
469
- policy: USRPPolicy
470
470
  root_handle: str
471
471
  capabilities: dict
472
472
  handles: dict[str, HandleEntry] = field(default_factory=dict)
@@ -569,10 +569,7 @@ class SessionManager:
569
569
  "the USRP is already open through a legacy v1 session"
570
570
  )
571
571
  live_schema = create()
572
- policy = USRPPolicy.load(gid=gid, serial=context.get("ident", ""))
573
- capabilities = live_schema.get_capabilities_v2(
574
- policy_restrictions=policy.restrictions
575
- )
572
+ capabilities = live_schema.get_capabilities_v2()
576
573
  capabilities["remote_resource_limits"] = {
577
574
  "maximum_sessions": _MAX_SESSIONS,
578
575
  "maximum_handles_per_session": _MAX_HANDLES_PER_SESSION,
@@ -601,7 +598,6 @@ class SessionManager:
601
598
  gid=gid,
602
599
  schema=live_schema,
603
600
  schema_v2=schema_v2,
604
- policy=policy,
605
601
  root_handle=root_handle,
606
602
  capabilities=capabilities,
607
603
  )
@@ -865,10 +861,6 @@ class SessionManager:
865
861
  args,
866
862
  positional_args,
867
863
  )
868
- policy_args = dict(args)
869
- if positional_args:
870
- policy_args["__args__"] = positional_args
871
- session.policy.check_control(method_name, policy_args)
872
864
  native_args = {
873
865
  key: self._resolve_handle_argument(session, value)
874
866
  for key, value in args.items()
@@ -884,7 +876,7 @@ class SessionManager:
884
876
  native_args,
885
877
  positional_args=native_positional,
886
878
  )
887
- except (RemoteRFPolicyError, RemoteRFProtocolError):
879
+ except RemoteRFProtocolError:
888
880
  raise
889
881
  except Exception as exc:
890
882
  raise RemoteRFNativeUHDError(
@@ -953,15 +945,6 @@ class SessionManager:
953
945
  except queue.Empty:
954
946
  return (False if mutate_metadata else None), mutations
955
947
 
956
- if not entry.type_id.endswith(("RXStreamer", "TXStreamer")):
957
- policy_args = dict(args)
958
- if positional_args:
959
- policy_args["__args__"] = positional_args
960
- session.policy.check_handle_call(
961
- entry.type_id,
962
- method_name,
963
- policy_args,
964
- )
965
948
  target = getattr(entry.value, method_name, None)
966
949
  if not callable(target):
967
950
  raise RemoteRFProtocolError(
@@ -1413,19 +1396,6 @@ class SampleDataV1Servicer(grpc_pb2_grpc.SampleDataV1Servicer):
1413
1396
  ):
1414
1397
  if not entry.type_id.endswith("TXStreamer"):
1415
1398
  raise RemoteRFStaleHandleError("TX data used a non-TX handle")
1416
- channels = entry.stream_channels or [0]
1417
- rates = []
1418
- get_rate = getattr(session.schema.device, "get_tx_rate", None)
1419
- if callable(get_rate):
1420
- for channel in channels:
1421
- rates.append(float(get_rate(channel)))
1422
- if not rates or min(rates) <= 0:
1423
- raise RemoteRFPolicyError(
1424
- "TX sample rate is unavailable for duty-cycle enforcement"
1425
- )
1426
- session.policy.check_tx_duration(
1427
- float(request.sample_count) / min(rates)
1428
- )
1429
1399
  if len(request.payload) != byte_count:
1430
1400
  raise RemoteRFProtocolError(
1431
1401
  "TX payload byte length does not match dtype/shape"
@@ -214,6 +214,18 @@ def _load_server_credentials(cert_dir: Path):
214
214
  )
215
215
 
216
216
 
217
+ def _print_tls_credentials_error(cert_dir: Path, error: Exception) -> None:
218
+ messages = (
219
+ "TLS certs/keys not found or unreadable.",
220
+ f"Expected in: {cert_dir}",
221
+ f"Fix: run serverrf --gen-certs {local_ip} --force",
222
+ "Or set REMOTERF_CERT_DIR to your cert folder.",
223
+ f"Details: {error}",
224
+ )
225
+ for message in messages:
226
+ printf(message, Sty.MAGENTA)
227
+
228
+
217
229
  def start_server_runtime(
218
230
  *,
219
231
  bind_host: str | None = None,
@@ -258,11 +270,7 @@ def start_server_runtime(
258
270
  try:
259
271
  server_credentials = _load_server_credentials(cert_dir)
260
272
  except Exception as e:
261
- printf("TLS certs/keys not found or unreadable.", Sty.RED)
262
- printf("Expected in: ", Sty.DEFAULT, f"{cert_dir}", Sty.MAGENTA)
263
- printf("Fix: run ", Sty.DEFAULT, "RRRFcerts --ip ", Sty.GREEN, f"{local_ip}", Sty.MAGENTA, " --force", Sty.DEFAULT)
264
- printf("Or set REMOTERF_CERT_DIR to your cert folder.", Sty.GRAY)
265
- print(f"\nDetails: {e}\n", file=sys.stderr)
273
+ _print_tls_credentials_error(cert_dir, e)
266
274
  raise SystemExit(2)
267
275
 
268
276
  if include_host_tunnel:
@@ -135,7 +135,7 @@ def _server_env_path() -> Path:
135
135
 
136
136
 
137
137
  def _devices_env_path() -> Path:
138
- # Legacy mirror retained for older tooling.
138
+ # Read-only migration source for pre-YAML installations.
139
139
  return _cfg_dir() / "devices.env"
140
140
 
141
141
 
@@ -156,11 +156,11 @@ def _gen_certs_path() -> Path:
156
156
  return (_repo_root() / "tools" / "gen_certs.py").resolve()
157
157
 
158
158
  def _gist_env_path() -> Path:
159
- # Stored alongside server.env/devices.env under ~/.config/remoterf by default
159
+ # Stored alongside server.env/devices.yml under ~/.config/remoterf by default
160
160
  return _cfg_dir() / "gist.env"
161
161
 
162
162
  def _tos_ack_path() -> Path:
163
- # Stored alongside server.env/devices.env under ~/.config/remoterf by default
163
+ # Stored alongside server.env/devices.yml under ~/.config/remoterf by default
164
164
  return _cfg_dir() / "tos.env"
165
165
 
166
166
 
@@ -262,6 +262,7 @@ def print_help() -> None:
262
262
  "\n"
263
263
  "Devices:\n"
264
264
  " serverrf -d | --device [options]\n"
265
+ " Inventory: ~/.config/remoterf/devices.yml (canonical YAML)\n"
265
266
  "\n"
266
267
  "Device options:\n"
267
268
  " --add --pluto <id:name:iio_serial> Add an ADALM-Pluto\n"
@@ -811,7 +812,11 @@ def _read_device_records() -> Dict[int, Dict[str, Any]]:
811
812
  recs[gid] = {
812
813
  "TYPE": dtype,
813
814
  "NAME": str(item.get("name") or f"device-{gid}").strip(),
814
- "IDENT_KIND": "iio_serial" if dtype == "pluto" else "serial",
815
+ "IDENT_KIND": (
816
+ "iio_serial"
817
+ if dtype in {"pluto", "adalm_pluto"}
818
+ else "serial"
819
+ ),
815
820
  "IDENT": ident,
816
821
  "INIT": dict(init),
817
822
  }
@@ -833,7 +838,6 @@ def _read_device_records() -> Dict[int, Dict[str, Any]]:
833
838
 
834
839
  def _write_device_records(recs: Dict[int, Dict[str, Any]]) -> None:
835
840
  devices: List[Dict[str, Any]] = []
836
- out: Dict[str, str] = {}
837
841
  for gid in sorted(recs.keys()):
838
842
  fields = recs[gid]
839
843
  dtype = str(fields.get("TYPE") or "").strip().lower()
@@ -852,22 +856,14 @@ def _write_device_records(recs: Dict[int, Dict[str, Any]]) -> None:
852
856
  "init": init,
853
857
  }
854
858
  )
855
- order = ["TYPE", "NAME", "IDENT_KIND", "IDENT"]
856
- for f in order:
857
- if f in fields and fields[f] != "":
858
- out[f"DEVICE_{gid}_{f}"] = str(fields[f])
859
- for f in sorted(fields.keys()):
860
- if f in order or f == "INIT":
861
- continue
862
- if fields[f] != "":
863
- out[f"DEVICE_{gid}_{f}"] = str(fields[f])
864
859
  _cfg_dir().mkdir(parents=True, exist_ok=True)
865
860
  _devices_yaml_path().write_text(
866
861
  yaml.safe_dump({"devices": devices}, sort_keys=False),
867
862
  encoding="utf-8",
868
863
  )
869
- # Maintain the original env representation for older CLI consumers.
870
- _write_env_kv(_devices_env_path(), out)
864
+ # Once a legacy inventory has been migrated successfully, remove it so
865
+ # there is exactly one authoritative device manifest.
866
+ _devices_env_path().unlink(missing_ok=True)
871
867
 
872
868
 
873
869
  def _device_add_pluto(spec: str) -> int:
@@ -894,14 +890,14 @@ def _device_add_pluto(spec: str) -> int:
894
890
  return 2
895
891
 
896
892
  recs[gid] = {
897
- "TYPE": "pluto",
893
+ "TYPE": "adalm_pluto",
898
894
  "NAME": name,
899
895
  "IDENT_KIND": "iio_serial",
900
896
  "IDENT": iio_serial,
901
897
  }
902
898
  _write_device_records(recs)
903
899
 
904
- print(f"Added device: {gid} type=pluto name={name} iio_serial={iio_serial}")
900
+ print(f"Added device: {gid} type=adalm_pluto name={name} iio_serial={iio_serial}")
905
901
  return 0
906
902
 
907
903
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -329,36 +329,10 @@ devices:
329
329
  serial: 31A2B3C
330
330
  ```
331
331
 
332
- Transmit is denied if `~/.config/remoterf/usrp_policy.yml` is missing,
333
- unreadable, invalid, or does not explicitly enable and bound the requested
334
- operation. Low-level writes are also denied unless both globally enabled and
335
- method/path/address allowlisted. A starting policy looks like:
336
-
337
- ```yaml
338
- defaults:
339
- tx_enabled: false
340
- low_level_write_enabled: false
341
-
342
- devices:
343
- "31A2B3C":
344
- tx_enabled: true
345
- tx_frequency_ranges_hz:
346
- - [902000000, 928000000]
347
- max_tx_gain_db: 20
348
- max_tx_normalized_gain: 0.5
349
- max_tx_power_dbm: -10
350
- max_tx_bandwidth_hz: 2000000
351
- max_tx_sample_rate_sps: 2000000
352
- allowed_tx_antennas: [TX/RX]
353
- max_burst_seconds: 1.0
354
- max_duty_cycle: 0.1
355
- duty_cycle_window_seconds: 60
356
- low_level_write_enabled: false
357
- low_level_write_methods: []
358
- gpio_write_masks: {}
359
- property_write_paths: []
360
- register_write_addresses: []
361
- ```
332
+ All USRP operations exposed by the Dynamic v2 schema are available without a
333
+ separate policy file. This includes transmit controls, transmit streaming,
334
+ GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
335
+ or required by the current server.
362
336
 
363
337
  On every qualified target, generate the introspection record and enforce the
364
338
  pinned API gate:
@@ -59,6 +59,30 @@ class CertificateProviderTests(unittest.TestCase):
59
59
  self.assertEqual(served, ca_pem)
60
60
  self.assertNotEqual(served, leaf_pem)
61
61
 
62
+ def test_missing_tls_guidance_is_visible_and_uses_serverrf(self):
63
+ from remoteRF_server.server import grpc_server
64
+
65
+ cert_dir = Path("/tmp/remoterf-test-certs")
66
+ error = FileNotFoundError("missing server.key")
67
+ with patch.object(grpc_server, "printf") as mock_printf:
68
+ grpc_server._print_tls_credentials_error(cert_dir, error)
69
+
70
+ rendered = [call.args for call in mock_printf.call_args_list]
71
+ self.assertEqual(
72
+ [message for message, _style in rendered],
73
+ [
74
+ "TLS certs/keys not found or unreadable.",
75
+ f"Expected in: {cert_dir}",
76
+ f"Fix: run serverrf --gen-certs {grpc_server.local_ip} --force",
77
+ "Or set REMOTERF_CERT_DIR to your cert folder.",
78
+ "Details: missing server.key",
79
+ ],
80
+ )
81
+ self.assertTrue(
82
+ all(style is grpc_server.Sty.MAGENTA for _message, style in rendered)
83
+ )
84
+ self.assertNotIn("RRRFcerts", "\n".join(message for message, _style in rendered))
85
+
62
86
 
63
87
  if __name__ == "__main__":
64
88
  unittest.main()