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.py168
1 files changed, 6 insertions, 162 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 f7bbbfa..acaa33f 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
@@ -5,17 +5,12 @@ import logging
from meshbay_common import MNP_VERSION
from meshbay_common.adminop import (
- OP_GROUP_ATTACH,
OP_GROUP_DETACH,
- OP_ROOT_ADD,
OP_ROOT_EJECT,
OP_ROOT_PLUG,
OP_ROOT_REMOVE,
- OP_ROOT_UPDATE,
OP_SET_SCAN_SETTINGS,
OP_TRANSFER_LIMITS,
- group_attach_subject,
- root_add_subject,
)
from meshbay_common.protocol import MNP
@@ -235,52 +230,6 @@ class NodeOpsMixin:
log.error("denylist_clear failed: %s", e, exc_info=True)
self._send({"type": "error", "detail": "Internal error"})
- def _do_group_attach(self, msg: dict) -> None:
- name = str(msg.get("name", "")).strip()
- shared_dir = str(msg.get("shared_dir", "")).strip()
- if not name or not shared_dir:
- self._send({"type": "error", "detail": "Missing name or shared_dir"})
- return
- if not self._has_admin_authority():
- self._send({"type": "error", "detail": "No authorized key for this"})
- return
- # `upload_dir` is not read here any more, and a client still sending it
- # is ignored rather than obeyed: on load it forces every other root
- # read-only, which is the model the RO/RW one replaced. A second
- # writable directory is `root_add` with `writable`.
- writable = bool(msg.get("writable", True))
- # The directory being exposed is signed, not only the group's name.
- self._issue_admin_challenge(
- OP_GROUP_ATTACH, group_attach_subject(name, shared_dir, writable),
- payload={"name": name, "shared_dir": shared_dir, "writable": writable},
- group_id="")
-
- async def _admin_exec_group_attach(
- 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_attach:{pending['subject'][:16]}")
- return
- p = pending.get("payload") or {}
- try:
- result = await self._run_op(
- ops.attach_group, p["name"], p["shared_dir"],
- writable=bool(p.get("writable", True)))
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- return
- self._audit("group_attach", pending["subject"])
- self._send({"type": MNP.GROUP_ATTACH_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_attach failed: %s", e)
-
def _do_group_detach(self, msg: dict) -> None:
name = str(msg.get("name", "")).strip()
if not name:
@@ -336,55 +285,6 @@ class NodeOpsMixin:
log.error("node_reload failed: %s", e, exc_info=True)
self._send({"type": "error", "detail": "Reload failed"})
- def _do_root_add(self, msg: dict) -> None:
- target_group = str(msg.get("group_id", "")).strip()
- path = str(msg.get("path", "")).strip()
- if not target_group or not path:
- self._send({"type": "error", "detail": "Missing group_id or path"})
- return
- if not self._has_admin_authority():
- self._send({"type": "error", "detail": "No authorized key for this"})
- return
- payload = {
- "group_id": target_group, "path": path,
- "name": str(msg.get("name", ""))[:128],
- "kind": str(msg.get("kind", "generic"))[:16],
- "writable": bool(msg.get("writable", msg.get("upload", False))),
- "removable": bool(msg.get("removable", False)),
- }
- # Everything the executor acts on is signed — `writable` decides whether
- # every member may write there. The group is in the transcript itself.
- self._issue_admin_challenge(
- OP_ROOT_ADD,
- root_add_subject(path, payload["name"], payload["kind"],
- payload["writable"], payload["removable"]),
- payload=payload, group_id=target_group)
-
- async def _admin_exec_root_add(
- 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"root_add:{pending['subject'][:24]}")
- return
- p = pending["payload"]
- try:
- result = await self._run_op(
- ops.add_root, p["group_id"], p["path"],
- name=p.get("name", ""), kind=p.get("kind", "generic"),
- writable=p.get("writable", False),
- removable=p.get("removable", False))
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- return
- except Exception as e:
- log.error("root_add failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
- return
- self._audit("root_add", f"{p['path']}→{p['group_id'][:8]}")
- await self._retarget_indexer(p["group_id"])
- self._send({"type": MNP.ROOT_ADD_ACK, "v": MNP_VERSION, **result})
-
def _do_root_remove(self, msg: dict) -> None:
target_group = str(msg.get("group_id", "")).strip()
root_name = str(msg.get("root_name", "")).strip()
@@ -421,59 +321,6 @@ class NodeOpsMixin:
await self._retarget_indexer(p["group_id"])
self._send({"type": MNP.ROOT_REMOVE_ACK, "v": MNP_VERSION, **result})
- def _do_root_update(self, msg: dict) -> None:
- target_group = str(msg.get("group_id", self._group_id or "")).strip()
- root_name = str(msg.get("root_name", "")).strip()
- if not target_group or not root_name:
- self._send({"type": "error", "detail": "Missing group_id or root_name"})
- return
- if not self._has_admin_authority():
- self._send({"type": "error", "detail": "No authorized key for this"})
- return
- updates = []
- if "writable" in msg:
- updates.append(f"rw={'on' if msg['writable'] else 'off'}")
- if "removable" in msg:
- updates.append(f"rem={'on' if msg['removable'] else 'off'}")
- subject = f"{root_name}:{','.join(updates)}" if updates else root_name
- self._issue_admin_challenge(
- OP_ROOT_UPDATE, subject,
- payload={
- "group_id": target_group, "root_name": root_name,
- "writable": msg.get("writable"),
- "removable": msg.get("removable"),
- },
- group_id=target_group)
-
- async def _admin_exec_root_update(
- 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"root_update:{pending['subject'][:24]}")
- return
- p = pending["payload"]
- try:
- result = await self._run_op(
- ops.update_root, p["group_id"], p["root_name"],
- writable=p.get("writable"), removable=p.get("removable"))
- except ops.OpError as e:
- self._send({"type": "error", "detail": e.message})
- return
- except Exception as e:
- log.error("root_update failed: %s", e, exc_info=True)
- self._send({"type": "error", "detail": "Internal error"})
- return
- self._audit("root_update", pending["subject"])
- await self._retarget_indexer(p["group_id"])
- notice = {"type": MNP.ROOT_UPDATE_ACK, "v": MNP_VERSION, **result}
- for uid, session in list(self._peer_registry().items()):
- try:
- session._send(notice)
- except Exception:
- pass
-
def _do_root_eject(self, msg: dict) -> None:
target_group = str(msg.get("group_id", self._group_id or "")).strip()
root_name = str(msg.get("root_name", "")).strip()
@@ -558,24 +405,21 @@ class NodeOpsMixin:
async def _retarget_indexer(self, group_id: str) -> None:
"""
- Pick up a root that was just added to or removed from node.toml.
+ Pick up a root that was just removed from node.toml.
Through the daemon's own reload, which is what the loopback API has
always done after the same operations (`ui/app.py`). This used to
re-point the indexer at `groups_ctx[gid]["roots"]` instead — the very
object the op had just edited — so `retarget` diffed a set against
- itself, found no new names, scanned nothing, and dropped nothing. A
- directory added over MNP reached node.toml and was invisible until a
- restart; one removed kept serving its files.
+ itself, found no new names, scanned nothing, and dropped nothing: a
+ directory removed over MNP kept serving its files until a restart.
Two front doors doing different things is the shape `ops.py` exists to
prevent, and this was it: the loopback path worked and the MNP path did
- not, which is why it survived until the operator added a directory from
- a browser.
+ not.
- Not awaited: a reload rescans, and a new library is minutes. The ack
- the caller sends carries the set the node is moving to, and the
- `index_sync` that follows the scan carries what it found.
+ Not awaited: a reload can be long. The ack the caller sends carries the
+ set the node is moving to.
"""
state = self._ctx.get("daemon_state")
if not state: