aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py43
1 files changed, 43 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 79a97cc..af41061 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -79,6 +79,7 @@ from meshbay_common.adminop import (
OP_CHAT_DIRECTORY,
OP_CHAT_EPOCH,
OP_CHAT_LINK_PREVIEW,
+ OP_SEARCH_LISTED,
OP_ROOT_ADD,
OP_ROOT_REMOVE,
OP_ROOT_UPDATE,
@@ -623,6 +624,8 @@ class WebRTCPeerSession:
self._do_chat_directory(msg)
elif mtype == MNP.CHAT_LINK_PREVIEW:
self._do_chat_link_preview(msg)
+ elif mtype == MNP.SEARCH_LISTED:
+ self._do_search_listed(msg)
elif mtype == MNP.CHAT_EPOCH:
self._do_chat_epoch(msg)
elif mtype == MNP.CHAT_KEYS_REQ:
@@ -949,6 +952,11 @@ class WebRTCPeerSession:
# on, which is what it did before this existed.
"chat_link_preview": bool(
self._group_ctx().get("chat_link_preview", True)),
+ # Whether the reader's cross-group Search should list this group.
+ # Presentation only: the index below is served to Search and to the
+ # group page alike, and this cannot tell them apart. Sealed like the
+ # rest, so the hub cannot flip it. Absent means listed.
+ "search_listed": bool(self._group_ctx().get("search_listed", True)),
# Which chat epoch key a client should be sealing under. Inside
# the sealed part of the ack like every other configuration field,
# so it carries an authentication tag from a key the hub does not
@@ -2392,6 +2400,38 @@ class WebRTCPeerSession:
self._broadcast_to_group({"type": MNP.CHAT_LINK_PREVIEW_ACK,
"v": MNP_VERSION, "enabled": enabled})
+ def _do_search_listed(self, msg: dict) -> None:
+ """
+ Whether this group's files appear in members' cross-group Search.
+ Signed because it changes what every member's Search shows, not
+ because it protects anything — see ops.set_search_listed.
+ """
+ listed = msg.get("listed")
+ if not isinstance(listed, bool):
+ self._send({"type": "error", "detail": "Missing or invalid 'listed'"})
+ return
+ if not self._has_admin_authority():
+ self._send({"type": "error", "detail": "No authorized key for this"})
+ return
+ self._issue_admin_challenge(OP_SEARCH_LISTED, "on" if listed else "off")
+
+ async def _admin_exec_search_listed(
+ self, pending: dict, transcript: bytes, sig: bytes,
+ ) -> None:
+ listed = pending["subject"] == "on"
+ if not await self._verify_admin_sig(transcript, sig):
+ self._send({"type": "error", "detail": "Signature verification failed"})
+ self._audit("admin_auth_failed", f"search_listed:{pending['subject']}")
+ return
+ try:
+ await self._run_op(ops.set_search_listed, self._group_id or "", listed)
+ except ops.OpError as e:
+ self._send({"type": "error", "detail": e.message})
+ return
+ self._audit("search_listed", pending["subject"])
+ self._broadcast_to_group({"type": MNP.SEARCH_LISTED_ACK,
+ "v": MNP_VERSION, "listed": listed})
+
def _do_chat_epoch(self, msg: dict) -> None:
"""
Open a new chat epoch by hand. Operator only, and signed.
@@ -5446,6 +5486,9 @@ class WebRTCPeerSession:
elif pending["op"] == OP_CHAT_LINK_PREVIEW:
self._spawn(
self._admin_exec_chat_link_preview(pending, transcript, sig_bytes))
+ elif pending["op"] == OP_SEARCH_LISTED:
+ self._spawn(
+ self._admin_exec_search_listed(pending, transcript, sig_bytes))
elif pending["op"] == OP_CHAT_EPOCH:
self._spawn(
self._admin_exec_chat_epoch(pending, transcript, sig_bytes))