aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py210
1 files changed, 4 insertions, 206 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py
index acaa33f..237a359 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc/node_ops.py
@@ -1,16 +1,16 @@
-"""The operator's controls over the node itself: status and settings, roster
-and denylist, roots, hosted groups, reload, scan pacing and transfer limits."""
+"""The operator's controls over a group's roots and scan pacing that MNP carries:
+removing, ejecting and plugging a root. What widens the sharing, and the node's
+own status, settings, roster and denylist, are the loopback API's and the CLI's
+(MNP 6.0)."""
import logging
from meshbay_common import MNP_VERSION
from meshbay_common.adminop import (
- OP_GROUP_DETACH,
OP_ROOT_EJECT,
OP_ROOT_PLUG,
OP_ROOT_REMOVE,
OP_SET_SCAN_SETTINGS,
- OP_TRANSFER_LIMITS,
)
from meshbay_common.protocol import MNP
@@ -55,64 +55,6 @@ class NodeOpsMixin:
self._issue_admin_challenge(
OP_SET_SCAN_SETTINGS, f"{reconcile:g},{debounce:g}")
- MIN_TRANSFER_LIMIT = 1
- MAX_TRANSFER_LIMIT = 32
-
- def _do_transfer_limits(self, msg: dict) -> None:
- """How many transfers one member may run at once in this group.
-
- Zero is not "unlimited" and is refused: a member who may not transfer at
- all is a member the operator revokes, and reading 0 as no-limit would
- make the most dangerous value the easiest to type by accident.
- """
- try:
- downloads = int(msg.get("downloads"))
- uploads = int(msg.get("uploads"))
- except (TypeError, ValueError):
- self._send({"type": "error", "detail": "Invalid transfer limits"})
- return
- for value in (downloads, uploads):
- if not (self.MIN_TRANSFER_LIMIT <= value <= self.MAX_TRANSFER_LIMIT):
- self._send({"type": "error",
- "detail": f"transfer limits must be between "
- f"{self.MIN_TRANSFER_LIMIT} and "
- f"{self.MAX_TRANSFER_LIMIT}"})
- return
- if not self._has_admin_authority():
- self._send({"type": "error", "detail": "No authorized key for this"})
- return
- self._issue_admin_challenge(OP_TRANSFER_LIMITS,
- f"d={downloads},u={uploads}")
-
- async def _admin_exec_transfer_limits(
- self, pending: dict, transcript: bytes, sig: bytes,
- ) -> None:
- try:
- parts = dict(p.split("=") for p in pending["subject"].split(","))
- downloads, uploads = int(parts["d"]), int(parts["u"])
- except (ValueError, KeyError):
- self._send({"type": "error", "detail": "Invalid transfer limits"})
- return
- if not await self._verify_admin_sig(transcript, sig):
- self._send({"type": "error", "detail": "Signature verification failed"})
- self._audit("admin_auth_failed", f"transfer_limits:{pending['subject']}")
- return
- try:
- result = await self._run_op(
- ops.set_transfer_limits, self._group_id or "", downloads, uploads)
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- return
- self._audit("transfer_limits", pending["subject"])
-
- notice = {"type": MNP.TRANSFER_LIMITS_ACK, "v": MNP_VERSION,
- "limits": result["limits"]}
- for session in list(self._peer_registry().values()):
- try:
- session._send(notice)
- except Exception:
- pass
-
async def _admin_exec_set_scan_settings(
self, pending: dict, transcript: bytes, sig: bytes,
) -> None:
@@ -141,150 +83,6 @@ class NodeOpsMixin:
except Exception:
pass
- # ── Node management (D5) ─────────────────────────────────────────────────
-
- async def _do_node_status(self, msg: dict) -> None:
- """All groups, roots, peers — the operator's overview.
-
- Including every root's absolute path, which is why this is gated on a
- proved operator device and not on an account the hub named.
- """
- node_uid = self._ctx.get("node_user_id")
- log.info("node_status: user=%s node_user=%s owner=%s device=%s",
- self._user_id, node_uid, self._is_node_admin(),
- "confirmed" if self._device_confirmed else "unidentified")
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- try:
- result = await self._run_op(ops.list_groups)
- self._send({"type": MNP.NODE_STATUS_ACK, "v": MNP_VERSION, **result})
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- except Exception as e:
- log.error("node_status failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
-
- async def _do_node_settings_set(self, msg: dict) -> None:
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- settings = msg.get("settings", {})
- if not settings:
- self._send({"type": "error", "detail": "No settings provided"})
- return
- try:
- result = await self._run_op(ops.set_node_settings, settings)
- self._send({"type": MNP.NODE_SETTINGS_SET_ACK, "v": MNP_VERSION,
- **result})
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- except Exception as e:
- log.error("node_settings_set failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
-
- async def _do_roster_read(self, msg: dict) -> None:
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- group_id = str(msg.get("group_id", "")).strip()
- try:
- result = await self._run_op(ops.read_roster, group_id)
- self._send({"type": MNP.ROSTER_READ_ACK, "v": MNP_VERSION, **result})
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- except Exception as e:
- log.error("roster_read failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
-
- async def _do_denylist_read(self, msg: dict) -> None:
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- try:
- result = await self._run_op(ops.read_denylist)
- self._send({"type": MNP.DENYLIST_READ_ACK, "v": MNP_VERSION, **result})
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- except Exception as e:
- log.error("denylist_read failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
-
- async def _do_denylist_clear(self, msg: dict) -> None:
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- subject = str(msg.get("subject", "")).strip()
- try:
- result = await self._run_op(ops.clear_denylist, subject=subject)
- self._audit("denylist_clear", subject or "all")
- self._send({"type": MNP.DENYLIST_CLEAR_ACK, "v": MNP_VERSION, **result})
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- except Exception as e:
- log.error("denylist_clear failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
-
- def _do_group_detach(self, msg: dict) -> None:
- name = str(msg.get("name", "")).strip()
- if not name:
- self._send({"type": "error", "detail": "Missing group name or id"})
- return
- if not self._has_admin_authority():
- self._send({"type": "error", "detail": "No authorized key for this"})
- return
- self._issue_admin_challenge(
- OP_GROUP_DETACH, name,
- payload={"name": name},
- group_id="")
-
- async def _admin_exec_group_detach(
- self, pending: dict, transcript: bytes, sig: bytes,
- ) -> None:
- if not await self._verify_admin_sig(transcript, sig):
- self._send({"type": "error", "detail": "Signature verification failed"})
- self._audit("admin_auth_failed",
- f"group_detach:{pending['subject'][:16]}")
- return
- p = pending.get("payload") or {}
- try:
- result = await self._run_op(ops.detach_group, p["name"])
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- return
- self._audit("group_detach", pending["subject"])
- self._send({"type": MNP.GROUP_DETACH_ACK, "v": MNP_VERSION, **result})
- state = self._ctx.get("daemon_state")
- reload_fn = state.get("reload_fn") if state else None
- if reload_fn:
- try:
- await reload_fn()
- except Exception as e:
- log.error("Reload after group_detach failed: %s", e)
-
- async def _do_node_reload(self, msg: dict) -> None:
- if not await self._operator_device():
- self._send({"type": "error", "detail": "Not the node operator",
- "code": "not_operator"})
- return
- state = self._ctx.get("daemon_state")
- reload_fn = state.get("reload_fn") if state else None
- if not reload_fn:
- self._send({"type": "error", "detail": "Reload not available"})
- return
- try:
- await reload_fn()
- self._send({"type": MNP.NODE_RELOAD_ACK, "v": MNP_VERSION,
- "status": "reloaded"})
- except Exception as e:
- log.error("node_reload failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Reload failed"})
-
def _do_root_remove(self, msg: dict) -> None:
target_group = str(msg.get("group_id", "")).strip()
root_name = str(msg.get("root_name", "")).strip()