Split out of #72 so its rationale is recorded honestly rather than assumed.
Current behaviour
Removing a member deletes their row from team_vault_keys (server/src/routes/teams.rs:545-550) and revokes their session grants, and the client evicts the in-memory vault key (src/services/sync.ts:882-886). The team DEK itself is never rotated, and the team blob is never re-encrypted.
What rotation would and would not buy
It is tempting to treat rotation as the fix for offboarding. It isn't, because a departing member held the plaintext:
| Exposure |
Protected today? |
Does rotation help? |
| Secrets they already read |
No — permanently compromised |
No. Only rotating the credentials themselves helps |
| Future team content |
Yes — membership row, wrapped key row and session grants all deleted server-side |
No, absent a server-authz bypass |
| Blob ciphertext they exfiltrated before leaving |
They held the DEK, so they can decrypt it |
No — a copy someone already holds cannot be retroactively re-encrypted |
| Plaintext left in their local keychain |
No — see #216 |
No |
Rotation protects only blobs written after rotation, which a removed member can no longer fetch anyway.
The case for doing it anyway
Defense in depth on the ciphertext at rest. Today a single DEK covers the entire lifetime of a team, so any leak of the encrypted blob — a database backup, a stale replica, a misconfigured storage bucket — exposes everything the team has ever stored, including data added long after a member left. Rotating on membership change bounds that blast radius to one membership epoch.
That is a real property, and it is worth stating as the reason. It is not an answer to "what happens to a removed member's access", which is #72's question.
Rough shape
- Generate a new team DEK, re-encrypt the team blob, re-wrap for every remaining member.
- Handle partial rotation: some members re-wrapped, some not, and the blob in one state or the other. This needs to be atomic or resumable — a half-rotated team where some members cannot decrypt is worse than no rotation.
- Handle offline members: someone who is not connected at rotation time must re-key on next connect without losing access in the meantime.
- Decide the trigger: automatic on every removal, or an explicit admin action. Automatic is safer but makes every removal a heavyweight operation.
- Decide what "rotate" does to older blob versions retained server-side, if any.
Related
Split out of #72 so its rationale is recorded honestly rather than assumed.
Current behaviour
Removing a member deletes their row from
team_vault_keys(server/src/routes/teams.rs:545-550) and revokes their session grants, and the client evicts the in-memory vault key (src/services/sync.ts:882-886). The team DEK itself is never rotated, and the team blob is never re-encrypted.What rotation would and would not buy
It is tempting to treat rotation as the fix for offboarding. It isn't, because a departing member held the plaintext:
Rotation protects only blobs written after rotation, which a removed member can no longer fetch anyway.
The case for doing it anyway
Defense in depth on the ciphertext at rest. Today a single DEK covers the entire lifetime of a team, so any leak of the encrypted blob — a database backup, a stale replica, a misconfigured storage bucket — exposes everything the team has ever stored, including data added long after a member left. Rotating on membership change bounds that blast radius to one membership epoch.
That is a real property, and it is worth stating as the reason. It is not an answer to "what happens to a removed member's access", which is #72's question.
Rough shape
Related
docs/superpowers/specs/2026-09-07-team-admin-offboarding-design.md