<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/test_account_deletion.py, branch 0.14</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.14</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.14'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-15T00:21:01Z</updated>
<entry>
<title>feat(hub): usernames are at least 8 characters at registration</title>
<updated>2026-09-15T00:21:01Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-15T00:16:39Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=bdefcd025604f2c3009fe5e0cc01213c2ba62a6a'/>
<id>urn:sha1:bdefcd025604f2c3009fe5e0cc01213c2ba62a6a</id>
<content type='text'>
Existing shorter accounts keep signing in. Test usernames padded to match.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm
</content>
</entry>
<entry>
<title>fix: bound what one member can cost the others</title>
<updated>2026-09-12T14:36:54Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-12T07:47:34Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=bf7ff9ec311660318c8562abe03ef4db62475c98'/>
<id>urn:sha1:bf7ff9ec311660318c8562abe03ef4db62475c98</id>
<content type='text'>
An availability review, prompted by the group claim above: a participant
supplies input — who else bears the cost? Six answers where the cost fell on
someone other than the sender, and none of them needs an attacker.

  AV3  `chat_notify` carried a `group_id` the hub believed, so any connected
       node could write a notification to every member of any group on the
       hub, carrying a display string of its choosing, with its account
       having no relation to that group. This is the group claim again, two
       hundred lines further down the same socket. Gated on what the node is
       registered for, and metered: the fan-out is one write per member. The
       budget expires by time rather than on disconnect, or reconnecting
       would refill it and a node token is good for an hour.

  AV4  A swarm source named its own `endpoint` as free text documented as
       "ip:port", so an account could publish a third party's address — H6's
       `peer_ip` defect, never applied here. Nothing dials a swarm source
       today, which is the only reason it was not already a reflection
       primitive. It is a transport and a port now, never a host, and the
       number of hashes one account may claim is bounded: rows were keyed
       (hash, account) with no cap at all.

  AV5  `handle_webrtc_answer` resolved any pending `peer_id` from any node's
       socket. The answer is the SDP a browser then connects to. That this
       had not happened rested on a uuid4 being unguessable.

  AV6  `relay_register` had no authentication of any kind: it compared
       `pk_relay` against the approved value, which is a *public* key, so
       anyone who could read it could rewrite where the hub tells nodes to
       send relayed traffic. The module docstring promised signed JWTs and
       `jwt` was imported and never used.

  AV7  The node held unlimited peer connections and kept one that never
       completed a handshake for the life of the daemon. H6 bounded what one
       unauthenticated peer costs; the hub's cap is three offers in flight
       per *account*, a limit on each caller and not on the machine, so an
       operator's exposure grew with the size of their groups.

  AV8  `invite-notify` put a request-supplied `group_name` into the subject
       of an email the hub sends under its own domain, to any account, with
       no rate limit. The name comes from the group row now.

The tests are two accounts each, in one file that says why: a one-member test
proves a one-member property, and every finding here needed a second person
to exist at all. Each was checked against the unfixed code. Two did not
survive that check and were rewritten — one re-enacted the disconnect path
instead of running it (hence `forget_node`), the other called the reaper
itself and would have passed with the call removed from `handle_offer`.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
</content>
</entry>
<entry>
<title>fix(hub): account deletion left device keys and swarm sources behind</title>
<updated>2026-09-11T09:25:38Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-11T09:25:38Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=e3c68b3416f8ea2c033d41ac263552833370ba18'/>
<id>urn:sha1:e3c68b3416f8ea2c033d41ac263552833370ba18</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
</content>
</entry>
<entry>
<title>feat(logs): keep the username on records the account no longer answers for</title>
<updated>2026-08-14T22:50:05Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-14T22:50:05Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=f4d04741379e77d2ef86cccc93856e590bfe1082'/>
<id>urn:sha1:f4d04741379e77d2ef86cccc93856e590bfe1082</id>
<content type='text'>
The connection log took the name from a join on `users`, and deletion
tombstones that row — so every record belonging to a deleted account
reported `deleted-3f9a1c`, which is the one answer that helps nobody. The
log is kept for a legal retention period precisely so it can say who did
what; losing the name at deletion kept the data and lost the point of it.

`ip_logs.username` is written as the account is erased, and stays NULL
while the account is alive, where the join is better because it cannot go
stale. The admin view prefers the stored name when there is one: the join
still answers after deletion, just with the tombstone.

Releasing the username for re-registration and keeping it in the log are
separate things, and the guide now says so.

On the node side, the pre-proof audit line records the username the
session already knew, instead of leaving the column empty.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(account): a user can delete their own account, an admin can delete one</title>
<updated>2026-08-14T20:41:02Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-14T20:41:02Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=8d8f85b4bf976249266a89f692408027216711b7'/>
<id>urn:sha1:8d8f85b4bf976249266a89f692408027216711b7</id>
<content type='text'>
Both go through the same erasure, so there is one description of what happens
rather than two that drift.

Gone: credentials, email, node key, group memberships, notifications, refresh
tokens, node registrations. The username is released.

Kept, on purpose and stated in the UI: the row itself, emptied, and the IP log
that points at it. Those logs exist for a year to answer legal requests, and a
log that can no longer say whose connection it recorded keeps the data while
losing the only thing it is for. So the account becomes a tombstone rather than a
hole in the table.

Out of reach, also stated: files uploaded to nodes, and the identity keys nodes
pinned. Those are on machines the hub does not command, and only their operators
can remove them — `member unpin` and a delete on their own disk. Saying so in the
confirmation matters more than the button.

Owning groups blocks deletion, with the list. Cascading would delete other
people's groups out from under them; the account holder can hand them over or
delete them first, deliberately.

Self-deletion re-checks the passphrase. A live token may be a borrowed laptop or
a tab left open, and it is not consent to something irreversible. Admin deletion
requires admin rather than moderator: suspension is the reversible moderation
tool and stays one click away.

A deleted account's access token stops working at once — the status check already
refuses anything but "active", which the tests now pin down, because refresh
tokens being gone would otherwise leave up to an hour of usable session.

Tests: 8 covering what survives and what does not, plus a db_session fixture for
assertions that cannot honestly be made through the API.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
