The app never actually created a sealed group #30

Merged
paulhitt merged 1 commits from 3-sealed-groups-ui into main 2026-08-31 15:24:59 +00:00
1 Commits
Author SHA1 Message Date
Paul HittandClaude Opus 4.8 0006d82de6 The app never actually created a sealed group
Everything underneath was built and tested and no screen called any of it, so
every group this app made still handed the relay one readable `gid -> user_id`
row per member. Production is four groups, four of them plaintext.

Making a group now seals it. The relay gets a blob it has no key for and one
opaque tag each.

Old groups get their own answer, because the migration is additive on purpose:
the relay keeps a plaintext roster until a client replaces it, so a group made
before sealing stays readable for ever unless something does it. The owner gets
a button. It keeps the gid — a new one would strand the thread, the history and
everyone else's copy — and reads the members out of the roster we already hold
rather than asking for them.

🔴 The group list now prefers the sealed roster, and that is the security-
relevant half. A sealed group's members come out of a blob the owner signed;
`GET /e2e/groups` carries no signature at all. Letting the relay's answer for
the same gid win — or merely shadow it — puts the server back in charge of
deciding who is in a group. Reverting just that merge fails the new test with
the relay's roster [1, 2, 99] replacing the blob's [1, 2].

Membership edits and leaving route on whether the group is sealed. Sending a
sealed group down the plaintext route asks the relay to edit a table it no
longer has: the call returns fine and the member is not there. One entry point,
so no screen has to branch to be correct.

The button says what it does and does not buy. It protects the list if the
database is stolen or handed over; it does not hide the group from whoever runs
the server, because messages are addressed to each device and who receives a
group's traffic still gives it away.

604 tests, 0 failures. assembleDebug succeeds.

Closes #3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 11:23:20 -04:00