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 4 groups, 4 plaintext, 0 sealed.
Making a group
createSealedGroup — a blob and one opaque tag per member
Old groups
upgradeGroupToSealed, owner-only, a button in the group panel
Group list
prefers the sealed roster, merges the unsealed ones
Edits / leaving
route on whether the group is sealed
The list merge 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,
which is what sealing exists to stop.
Proven by reverting just that merge:
SealedGroupListTest > the relay cannot add a member to a sealed group FAILED
AssertionError: the relay-s roster replaced the one out of the blob
expected:<[1, 2]> but was:<[1, 2, 99]>
SealedGroupListTest > a sealed group is listed at all FAILED
expected:<[1, 2]> but was:<[]>
Why edits route
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 in Session, so no screen has to branch to be correct — same shape as iOS e90ba1b.
Copy
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.
paulhitt/flamenet-relay#2 — four production rosters that can now actually be
sealed from a phone people already have, rather than waiting on Apple.
Closes #3
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 **4 groups, 4 plaintext, 0 sealed**.
| | |
|---|---|
| Making a group | `createSealedGroup` — a blob and one opaque tag per member |
| Old groups | `upgradeGroupToSealed`, owner-only, a button in the group panel |
| Group list | prefers the sealed roster, merges the unsealed ones |
| Edits / leaving | route on whether the group is sealed |
### The list merge 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,
which is what sealing exists to stop.
Proven by reverting just that merge:
```
SealedGroupListTest > the relay cannot add a member to a sealed group FAILED
AssertionError: the relay-s roster replaced the one out of the blob
expected:<[1, 2]> but was:<[1, 2, 99]>
SealedGroupListTest > a sealed group is listed at all FAILED
expected:<[1, 2]> but was:<[]>
```
### Why edits route
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 in `Session`, so no screen has to branch to be correct — same shape as iOS
`e90ba1b`.
### Copy
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.
### Verification
```
engine 273 tests, 0 failures
client 277 tests, 0 failures
app 54 tests, 0 failures
TOTAL 604 tests, 0 failures
:app:assembleDebug BUILD SUCCESSFUL
```
### What this unblocks
paulhitt/flamenet-relay#2 — four production rosters that can now actually be
sealed from a phone people already have, rather than waiting on Apple.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #3
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_idrow per member. Production is 4 groups, 4 plaintext, 0 sealed.
createSealedGroup— a blob and one opaque tag per memberupgradeGroupToSealed, owner-only, a button in the group panelThe list merge is the security-relevant half
A sealed group’s members come out of a blob the owner signed.
GET /e2e/groupscarries 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,
which is what sealing exists to stop.
Proven by reverting just that merge:
Why edits route
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 in
Session, so no screen has to branch to be correct — same shape as iOSe90ba1b.Copy
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.
Verification
What this unblocks
paulhitt/flamenet-relay#2 — four production rosters that can now actually be
sealed from a phone people already have, rather than waiting on Apple.
mentioned in commit
0034a57389changed the description