aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-11 11:25:38 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-11 11:25:38 +0200
commite3c68b3416f8ea2c033d41ac263552833370ba18 (patch)
treeb0549de294ea5c66466152d6c9465826633323f9 /packages/meshbay-hub/src/meshbay_hub/api
parente0607a9cee6ce9596e775baaed43eb67e1e5a293 (diff)
downloadmeshbay-e3c68b3416f8ea2c033d41ac263552833370ba18.tar.gz
fix(hub): account deletion left device keys and swarm sources behind
erase_account cleared memberships, notifications, tokens and node registrations, but not user_devices or swarm_sources. A device key left on the tombstone still belonged to it, so an account created later from the same desktop installation - which keeps its private half - was refused that device with a 409 that only reached the console. swarm_sources is keyed by the user id despite its column name and carries the node's ip:port. Both are now erased, which is what the privacy statement promises: every account row goes except the one-year IP log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py13
1 files changed, 11 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index 56208a0..f291f59 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -33,7 +33,7 @@ from meshbay_hub.config import HubConfig
from meshbay_hub.db.engine import get_db
from meshbay_hub.db.models import (
EmailVerification, Group, GroupMember, IPLog, Node, Notification,
- RefreshToken, User, UserDevice, UserPreference,
+ RefreshToken, SwarmSource, User, UserDevice, UserPreference,
)
log = logging.getLogger(__name__)
@@ -1084,7 +1084,14 @@ async def erase_account(db: AsyncSession, user: User) -> dict:
Erase an account, keeping only what the law asked us to keep.
Gone: credentials, email, node key, group memberships, notifications, refresh
- tokens, node registrations. The username is released.
+ tokens, node registrations, device keys, public-swarm sources. The username
+ is released.
+
+ Device keys go even though the desktop client keeps its private half: left
+ behind, the key still belongs to this tombstone, so an account created later
+ from the same installation is refused that device ("belongs to another
+ account"). Swarm sources are keyed by the *user* id and carry the node's
+ ip:port.
Kept: the row itself, emptied, and the IP log that points at it. Those logs
exist for one year to answer legal requests, and a log that cannot say whose
@@ -1112,6 +1119,8 @@ async def erase_account(db: AsyncSession, user: User) -> dict:
await db.execute(delete(Notification).where(Notification.user_id == user.id))
await db.execute(delete(RefreshToken).where(RefreshToken.user_id == user.id))
await db.execute(delete(Node).where(Node.user_id == user.id))
+ await db.execute(delete(UserDevice).where(UserDevice.user_id == user.id))
+ await db.execute(delete(SwarmSource).where(SwarmSource.node_id == user.id))
await db.execute(delete(EmailVerification).where(EmailVerification.user_id == user.id))
username = user.username