aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-21 01:35:13 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-21 01:35:13 +0200
commit03bcfcdef2ff9a3d6ed0776f7580a45a5401ef37 (patch)
tree84fa39ced04e0d82da1f40176e218b200c274761 /packages/meshbay-hub/tests
parent90e26beff212e4994b8e4f18c8260f2aa8a9b40c (diff)
downloadmeshbay-03bcfcdef2ff9a3d6ed0776f7580a45a5401ef37.tar.gz
fix(hub): removing from a group no longer unpins the account
The node half called `unpin` after `revoke`, and an unpin takes no group: it deletes the identity and every member row the account holds on this node, and drops the stored keypair bundle with them. Taking somebody out of one group took them out of all of them, silently. `revoke` is group-scoped and is what withdraws the key. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/test_member_removal.py21
1 files changed, 21 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_member_removal.py b/packages/meshbay-hub/tests/test_member_removal.py
index 609ef6a..7af73a0 100644
--- a/packages/meshbay-hub/tests/test_member_removal.py
+++ b/packages/meshbay-hub/tests/test_member_removal.py
@@ -53,6 +53,27 @@ def test_the_hub_half_runs_even_when_the_node_refuses():
"member on the hub, in this list and in every other session")
+def test_removing_from_one_group_does_not_unpin_the_account():
+ """
+ `unpin` takes no group. `roster.unpin` deletes the identity and *every*
+ member row the account holds on this node, and `ops.unpin_member` drops the
+ stored keypair bundle with them — so calling it from a per-group removal
+ took the person out of every other group on the node as well. Silently:
+ the loopback path writes no audit entry, so the node's journal showed a
+ clean join and then, hours later, a refusal with nothing in between. And
+ unrecoverably for anyone whose invitation was already spent, since the way
+ back starts with a code the operator has to issue again.
+
+ Forgetting a pinned key is a separate operator decision with its own button
+ on the node page. If one is ever wanted here, it has to be scoped to the
+ group first.
+ """
+ body = _remove_member_body()
+ assert "/unpin" not in body, (
+ "a per-group removal unpins the account node-wide: every other group "
+ "this person holds on this node goes with it")
+
+
def test_a_refusal_is_still_reported():
"""
Swallowing it would be the opposite mistake — the group key is what the