From b5b4f188a39fc96c4d32e67151e067b1add6dcfc Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 28 Aug 2026 02:51:00 +0200 Subject: feat(hub): let a hub admin disable public groups instance-wide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new General tab in Administration carries one switch, allow_public_groups, stored in a hub_settings key/value table (runtime-editable, unlike hub.toml). Default is on; an absent row means on, so an upgrade changes nothing. Enforcement is server-side on every hub-mediated path, not just the SPA: - create_group refuses visibility=public (403), staff included - list_public_groups the directory returns nothing (local + federated) - join_group open-joining a public group is refused - group_online_nodes a non-member of a public group is handed no node - signaling.webrtc_offer drops the "node hosts an open group" fallback - federation.export_directory advertises nothing to peer hubs The switch is read live, so flipping it back restores every path. Existing members of a group that predates the switch keep their membership row and their access — this is plan A, not a purge. GET /v1/hub/info exposes the flag (unauthenticated) so the create-group form and the sidebar's "Public groups" link render correctly. Also in the admin Groups tab: a Revoke action beside Suspend. Suspend is the reversible hub flag; Revoke calls POST /v1/admin/revoke, which sets status=revoked and broadcasts a signed revocation every node enforces (denylist + dropped live sessions). It is confirm-guarded and names the group. And a message fix the revoke work surfaced: group_online_nodes, join_group and webrtc_offer answered "Group is suspended" for any non-active status. They now report the real state, so a member of a revoked group is told "Group is revoked" rather than something reversible-sounding. Tests: test_public_groups_toggle.py (10) covers the switch end to end and the five enforcement paths; test_revocation.py gains the status-message assertion. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018gKJ85aZyvEwarXMFzFEwi --- packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 48b10fb..ccfc709 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -351,6 +351,11 @@ export default { + 'celle que le node croit être la sienne, apprise d’un serveur STUN puis ' + 'transmise ; elle sert à joindre le node et ne prouve rien.', 'admin.tab_blocklist': 'Liste de blocage', + 'admin.tab_general': "Général", + 'admin.general_groups_heading': "Groupes", + 'admin.allow_public_groups_label': "Autoriser les membres à créer des groupes publics", + 'admin.allow_public_groups_hint': "Désactivé, les nouveaux groupes ne peuvent être que privés et sur invitation. Les groupes publics existants ne sont pas affectés — suspendez-les un par un depuis l'onglet Groupes.", + 'admin.settings_readonly': "Seul un administrateur peut modifier ces paramètres.", // Admin stats 'admin.stat_users': 'Utilisateurs', @@ -370,6 +375,8 @@ export default { 'admin.no_users': 'Aucun utilisateur trouvé', 'admin.btn_suspend': 'Suspendre', 'admin.btn_unsuspend': 'Réactiver', + 'admin.btn_revoke': "Révoquer", + 'admin.revoke_group_confirm': "Révoquer le groupe « {group} » ? Une révocation signée est envoyée à chaque node qui l'héberge, et c'est irréversible depuis cette page. Suspends-le plutôt si tu veux seulement le mettre en pause.", 'admin.btn_details': 'Détails', 'admin.btn_delete': 'Supprimer', 'admin.delete_confirm': 'Supprimer le compte « {user} » ? Cette action est ' -- cgit v1.2.3