summaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-11 19:27:27 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-11 19:27:27 +0200
commitd8885c8df17c60927cb8d1f77ce1745814c6d3b4 (patch)
treefafa41edfc7adcb25d6c46d4bbf2d0fba93ed010 /docs/MESHBAY_DESIGN.md
parentedbff1768054afa80efda721cd1011b29c7fe355 (diff)
downloadmeshbay-d8885c8df17c60927cb8d1f77ce1745814c6d3b4.tar.gz
fix(hub): an administrator can erase an account that owns groups
An administrator's deletion answered 409 for any account owning a group, so an erasure ordered by an authority had to wait on the person it was about. It now deletes the account's groups with it, then pushes a signed revocation for the account and for each group to every connected node: an access token already issued stays valid on a node until it expires, and the revocation is what makes the nodes refuse the account and close the groups' sessions now. The action is written to the IP log, and the confirmation dialog says the groups go too, in all ten catalogues. The owner's own deletion is unchanged: refused while they own groups, which they can hand over first (CGU 3.4, privacy statement). Deleting a group had three partial cascades. The owner's route left email_verifications behind, and the cleanup of unhosted groups left notifications, invitations and reports - each an IntegrityError on PostgreSQL, invisible on SQLite, which does not enforce foreign keys by default. db/purge.py is now the one implementation: it finds every table referencing groups.id from the schema, deletes the group's rows and detaches content reports, which are evidence and outlive the group. test_group_purge.py turns foreign-key enforcement on for its connection, seeds every referencing table, and fails without the fix on all three routes. MESHBAY_DESIGN.md 7.7 states the rule, and now lists the device keys and swarm sources that e3c68b3 erases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md24
1 files changed, 19 insertions, 5 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 472d5ab..39a24da 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -1615,9 +1615,11 @@ A user can delete their own account from Settings behind a **passphrase re-entry
radius is proof of the passphrase. An admin can delete one too.
The row is **tombstoned rather than dropped**: username released, email and password
-hash cleared, node linking key dropped, memberships, notifications and refresh
-tokens removed, active tokens refused at once by a status check rather than left to
-expire.
+hash cleared, node linking key dropped, memberships, notifications, refresh tokens,
+node registrations, device keys and public-swarm sources removed, active tokens
+refused at once by a status check rather than left to expire. Device keys go
+because the desktop client keeps its half: left on the tombstone, the key would
+refuse that installation to the next account created from it.
Two things survive on purpose:
@@ -1630,8 +1632,20 @@ Two things survive on purpose:
work. **Deleting a hub account is not an erasure request to the operators who
host you**; the operator surface is where that happens, and the docs must say so.
-Deletion is **refused while the account still owns groups**, rather than cascading
-into other people's data.
+**Groups the account owns** decide between the two routes:
+
+- **The owner's own deletion is refused** while the account still owns groups,
+ rather than cascading into other people's data — the owner can hand them over
+ first.
+- **An administrator's deletion deletes them with the account.** It is the route
+ an erasure ordered by an authority takes, and it cannot wait on the person it is
+ about. Everything on the hub that references those groups goes too
+ (`db/purge.py`, which finds the referencing tables from the schema, so none is
+ left to fail a foreign key on PostgreSQL). Then a **signed revocation** for the
+ account and for each group goes to every connected node: an access token already
+ issued stays valid on a node until it expires, and the revocation is what makes
+ the nodes refuse the account and close the groups' sessions now. A node that is
+ offline misses it.
Registration is gated by a CAPTCHA whenever one is configured — **unconditionally**,
not only when some other field is absent, or the real client's ordinary request