What happens today
S-C51 (PR #458) widened album writes from the owner to writer members. A writer member's upload is:
- filed under the album owner's namespace (
owner_id), because the owner's feed is the one every member's devices read; and
- billed to the uploader (
upload_user_id) — capsule-server/src/routes/upload.rs, and design/server.md's "owner_id is the namespace and upload_user_id is billed".
Publishing a later roster that omits that member changes neither fact. The asset stays in the owner's album, and its bytes stay attributed to the removed member in the quota ledger.
The ledger is not wrong — the bytes are still stored — but the account they are charged against can no longer reach them: a removed member may not write ops to that album, so they cannot delete their way back under quota. The only path that ever releases the attribution is the refcount collector (capsule-server/src/gc/mod.rs, QuotaStore::release_attribution, S-C44), which runs when the last reference to the bytes goes, i.e. only if the owner deletes the asset.
Why it was not fixed in #458
Reclaiming on removal is a protocol question no design document in this tree answers:
- does the owner inherit the storage cost of what a departed member contributed?
- does the member keep paying for bytes the owner still holds and still reads?
- is removal from a roster a deletion at all, or explicitly not one (the MLS side treats it as a key rotation, not a data event)?
Each answer is a different product decision, and two of them let one account's quota pressure delete another account's photos. Inventing one inside a storage port would be the wrong place to decide it.
What a fix would need
- A decision recorded in
design/quota.md and design/authorization.md about who owns the cost of a departed member's contributions.
- Whatever seam that decision implies — a re-attribution on
apply_roster, an owner-initiated adoption, or an explicit "no reclaim, by design" note. QuotaStore already has both halves it would need (charge is idempotent per address; release_attribution releases by address to whoever holds it), so the port does not need widening for any of the three.
- Conformance coverage for whichever is chosen, in
capsule-server/src/quota/conformance.rs.
Where it is currently recorded
capsule-server/src/membership/mod.rs module docs ("Removing a member reclaims nothing, and that is observable") and capsule-server/src/routes/roster.rs.
Refs #405. Found by review of PR #458.
What happens today
S-C51(PR #458) widened album writes from the owner to writer members. A writer member's upload is:owner_id), because the owner's feed is the one every member's devices read; andupload_user_id) —capsule-server/src/routes/upload.rs, anddesign/server.md's "owner_id is the namespace and upload_user_id is billed".Publishing a later roster that omits that member changes neither fact. The asset stays in the owner's album, and its bytes stay attributed to the removed member in the quota ledger.
The ledger is not wrong — the bytes are still stored — but the account they are charged against can no longer reach them: a removed member may not write ops to that album, so they cannot delete their way back under quota. The only path that ever releases the attribution is the refcount collector (
capsule-server/src/gc/mod.rs,QuotaStore::release_attribution,S-C44), which runs when the last reference to the bytes goes, i.e. only if the owner deletes the asset.Why it was not fixed in #458
Reclaiming on removal is a protocol question no design document in this tree answers:
Each answer is a different product decision, and two of them let one account's quota pressure delete another account's photos. Inventing one inside a storage port would be the wrong place to decide it.
What a fix would need
design/quota.mdanddesign/authorization.mdabout who owns the cost of a departed member's contributions.apply_roster, an owner-initiated adoption, or an explicit "no reclaim, by design" note.QuotaStorealready has both halves it would need (chargeis idempotent per address;release_attributionreleases by address to whoever holds it), so the port does not need widening for any of the three.capsule-server/src/quota/conformance.rs.Where it is currently recorded
capsule-server/src/membership/mod.rsmodule docs ("Removing a member reclaims nothing, and that is observable") andcapsule-server/src/routes/roster.rs.Refs #405. Found by review of PR #458.