From cd2745cecff12e894e0dfa702bff6a90f0e8734e Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 14 Sep 2026 21:45:53 +0200 Subject: feat: a group can be left out of Search, and Search tries every node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `search_listed` is a per-group setting on the node, changed by a signed operator op and carried in the sealed handshake ack. Search reads it after the handshake and stops there: no index is fetched, cached or merged, in any of the four views, and the page says how many groups it left out. The switch is a "Search" section in the group's settings, shown to the operator. Absent means listed, at every layer: roster default, ack default, and the client only drops a group on an explicit `false` — so an upgrade or an older node removes nothing from anyone's Search. It is a listing preference and protects nothing: the node serves the same index to Search and to the group page and cannot tell them apart, every member lists the group by opening it, and a client that ignores the flag lists it in Search too. Design §9.11 says so, so it is never described as private. The cost is one handshake per unlisted group, because only the node knows the setting. Search also took `nodes[0]` twice — for the index and for the pooled connection — the defect 4cce50f fixed on the group page only. One `connectToGroup` now walks the list the same way: a refusal about this browser stops, `not_hosted` or a failed connection moves on. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm --- .../src/meshbay_hub/static/transport.js | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index 9de5407..1f05b8d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -96,7 +96,7 @@ const BROADCAST_ACK_TYPES = new Set([ 'root_update_ack', 'root_eject_ack', 'root_plug_ack', 'root_add_ack', 'root_remove_ack', 'app_directories_ack', 'chat_directory_ack', 'chat_link_preview_ack', - 'chat_epoch_ack', + 'chat_epoch_ack', 'search_listed_ack', ]); /** Hand a broadcast ack to the callback that would have had it from a peer. */ @@ -107,6 +107,8 @@ function _replayBroadcast(transport, msg) { transport._onChatDirectory(msg.path || ''); } else if (msg.type === 'chat_link_preview_ack' && transport._onChatLinkPreview) { transport._onChatLinkPreview(Boolean(msg.enabled)); + } else if (msg.type === 'search_listed_ack' && transport._onSearchListed) { + transport._onSearchListed(msg.listed !== false); } else if (msg.type === 'chat_epoch_ack') { transport._applyChatEpoch(msg); } else if (transport._onRootsChanged) { @@ -120,7 +122,7 @@ const ADMIN_OP_TYPES = new Set([ 'apps_enabled', 'set_scan_settings', 'member_revoke', 'root_add', 'root_remove', 'root_update', 'root_eject', 'root_plug', 'app_directories', 'chat_directory', 'chat_link_preview', 'chat_epoch', - 'member_unpin', 'gek_rotate', 'group_attach', + 'search_listed', 'member_unpin', 'gek_rotate', 'group_attach', 'group_detach', 'invite_create', ]); @@ -533,6 +535,7 @@ class MeshBayTransport { set onAppDirectories(fn) { this._onAppDirectories = fn; } set onChatDirectory(fn) { this._onChatDirectory = fn; } set onChatLinkPreview(fn) { this._onChatLinkPreview = fn; } + set onSearchListed(fn) { this._onSearchListed = fn; } set onChatEpoch(fn) { this._onChatEpoch = fn; } set onTmdbConfig(fn) { this._onTmdbConfig = fn; } set onTmdbEnabled(fn) { this._onTmdbEnabled = fn; } @@ -1525,6 +1528,22 @@ class MeshBayTransport { return msg; } + /** + * Whether this group's files appear in members' cross-group Search. + * Presentation only — opening the group lists everything regardless. + */ + async setSearchListed(listed, signFn) { + const msg = await this._sendAndWait({ + type: 'search_listed', v: '3.0', listed: Boolean(listed), + }); + if (msg.type === 'error') throw new Error(msg.detail); + if (msg.type === 'admin_challenge') { + return this._authorizeAdminOp( + msg, 'search_listed', listed ? 'on' : 'off', signFn); + } + return msg; + } + /** * Open a new chat epoch by hand. Operator only, and signed. * @@ -3097,6 +3116,9 @@ class MeshBayTransport { if (msg.type === 'chat_link_preview_ack' && this._onChatLinkPreview) { this._onChatLinkPreview(Boolean(msg.enabled)); } + if (msg.type === 'search_listed_ack' && this._onSearchListed) { + this._onSearchListed(msg.listed !== false); + } if (msg.type === 'chat_epoch_ack') { this._applyChatEpoch(msg); return; -- cgit v1.2.3