What happens to shares when an account is removed? (research + decision) #569

Closed
opened 2026-09-17 01:13:40 +00:00 by Cordy · 3 comments
Owner

Split out of #529's mockup review (2026-09-17). Nikola: "for now we have 'an admin can see them' in place, but the instinct to get a decision on this is right."

The question

When an account is removed — a local account deleted, or a directory account that stops existing — what happens to the shares that account created?

Once #529 gives share.Store an addressee, every share has two identities that can disappear: the creator and the addressee. The failure modes are different and both need an answer:

Disappears Today After #529
Creator local account deleted, or directory account gone share record survives, CreatedBy dangles same, but the grant it carries now means something
Addressee user removed, or group deleted n/a — no addressees exist yet must fail closed (already decided in #529: stops resolving, nothing mutated, the row says so)

The addressee half is settled. This issue is about the creator half, which is not.

What Cairn does today

Nothing deliberate. share.Share carries CreatedBy as a string; Store.List(ctx, createdBy) filters by it. Removing the account does not touch the shares. So the shares keep working, owned by a name that no longer resolves, and the only way to find them is an admin listing.

That is accidentally the fail-closed-ish answer, but it was never chosen, and it has a sharp edge worth naming: a public link created by a departed employee keeps serving their files. Nobody is notified, nothing expires, and the file is still readable by anyone holding the URL. For a product whose pitch is that the instance admin is accountable for what is on their own hardware, that is the wrong default to arrive at by omission.

There is a second-order problem specific to Cairn: after #529, a read goes through the owner's key scope. If the owner is gone, whose scope opens the file? The recovery identity presumably — but that path needs to be deliberate, not incidental, and it interacts with key custody (#528) and the retained-key rotation story.

Deep research task

Use Superpowers. Compare ownCloud, oCIS, Nextcloud and Seafile:

  1. What each actually does on user deletion — are shares deleted, transferred, orphaned, or frozen? Is there a "transfer ownership" tool, and is it manual or automatic? What happens to public links specifically, as distinct from user/group shares?
  2. The pitfalls they hit. Nextcloud's file-transfer-ownership command and its known limits are a starting point; ownCloud's admin manual already warns that deleting a user destroys everything they shared. Find the bug reports and forum threads where the behaviour surprised somebody.
  3. What users say. Admin complaints, data-loss reports, "where did my files go" threads. The same lens used for the #550 Used-by research, which found that admins were being told to query the database directly.
  4. What Cairn could do better, given that its differentiator is admin accountability and a real audit log — and given the key-scope constraint above, which none of the competitors have because none of them key encryption by path.

Then propose options with a recommendation, the way #529's crypto question was handled: state the constraint first, lay out the two or three real choices, recommend one, and name the consequence that has to be said out loud rather than buried.

Scope

Research and a recorded decision. Implementation is a follow-up once the decision exists — it may well be small (an admin listing plus a defined rule), but the rule has to be chosen before it is coded.

Blocked by nothing. Wants to be settled before the closed beta, since the first real user deletion will otherwise settle it by accident.

Split out of #529's mockup review (2026-09-17). Nikola: *"for now we have 'an admin can see them' in place, but the instinct to get a decision on this is right."* ## The question When an account is removed — a local account deleted, or a directory account that stops existing — what happens to the shares that account created? Once #529 gives `share.Store` an addressee, every share has **two** identities that can disappear: the **creator** and the **addressee**. The failure modes are different and both need an answer: | | Disappears | Today | After #529 | |---|---|---|---| | **Creator** | local account deleted, or directory account gone | share record survives, `CreatedBy` dangles | same, but the grant it carries now means something | | **Addressee** | user removed, or group deleted | n/a — no addressees exist yet | must fail closed (already decided in #529: stops resolving, nothing mutated, the row says so) | The addressee half is settled. **This issue is about the creator half**, which is not. ## What Cairn does today Nothing deliberate. `share.Share` carries `CreatedBy` as a string; `Store.List(ctx, createdBy)` filters by it. Removing the account does not touch the shares. So the shares keep working, owned by a name that no longer resolves, and the only way to find them is an admin listing. That is *accidentally* the fail-closed-ish answer, but it was never chosen, and it has a sharp edge worth naming: **a public link created by a departed employee keeps serving their files.** Nobody is notified, nothing expires, and the file is still readable by anyone holding the URL. For a product whose pitch is that the instance admin is accountable for what is on their own hardware, that is the wrong default to arrive at by omission. There is a second-order problem specific to Cairn: after #529, a read goes through the **owner's key scope**. If the owner is gone, whose scope opens the file? The recovery identity presumably — but that path needs to be deliberate, not incidental, and it interacts with key custody (#528) and the retained-key rotation story. ## Deep research task Use Superpowers. Compare **ownCloud**, **oCIS**, **Nextcloud** and **Seafile**: 1. **What each actually does** on user deletion — are shares deleted, transferred, orphaned, or frozen? Is there a "transfer ownership" tool, and is it manual or automatic? What happens to public links specifically, as distinct from user/group shares? 2. **The pitfalls they hit.** Nextcloud's file-transfer-ownership command and its known limits are a starting point; ownCloud's admin manual already warns that deleting a user destroys everything they shared. Find the bug reports and forum threads where the behaviour surprised somebody. 3. **What users say.** Admin complaints, data-loss reports, "where did my files go" threads. The same lens used for the #550 Used-by research, which found that admins were being told to query the database directly. 4. **What Cairn could do better**, given that its differentiator is admin accountability and a real audit log — and given the key-scope constraint above, which none of the competitors have because none of them key encryption by path. Then propose options with a recommendation, the way #529's crypto question was handled: state the constraint first, lay out the two or three real choices, recommend one, and name the consequence that has to be said out loud rather than buried. ## Scope Research and a recorded decision. Implementation is a follow-up once the decision exists — it may well be small (an admin listing plus a defined rule), but the rule has to be chosen before it is coded. Blocked by nothing. Wants to be settled before the closed beta, since the first real user deletion will otherwise settle it by accident.
Author
Owner

Research done — four products, two failure families, options + recommendation below

1. What each product actually does on user deletion

Nextcloud — deleting a user deletes their files and every share they created, public links included. The escape hatch is occ files:transfer-ownership, run manually before deletion; it moves files plus outgoing shares into a subfolder of the target user. Trash is not transferred, incoming shares are not moved by default. There is no automatic step, no warning in the deletion flow, and admins who deleted first and read the manual second show up in the forums asking how to restore from backup.

ownCloud classic — same model; the admin manual warns outright that deleting a user destroys everything they shared. There is a cleanup-orphaned-shares maintenance command, which tells you orphans happen often enough to need a broom.

oCIS — the opposite failure: deletion orphans content instead of destroying it. A deleted user's personal and project spaces keep existing (#4195), the admin cannot access or delete them (#4194, #4196), and removing a space's only Manager leaves the space unmanageable by anyone including admins (#4105, #7265). Undead data nobody controls — on the product whose whole redesign was supposed to fix this class.

Seafile — library-centric: deleting a library cancels all shares in it (restorable from admin trash); user deletion is handled by admins transferring libraries, with its own bug tail — ownership transfer drops assigned user/group permissions (forum 16706), transferring groups from a deleted user misbehaves (forum 14484).

2. The pitfalls they hit

The transfer tool is where Nextcloud bleeds: public shares silently deleted on transfer (#60223, root cause a cascade-delete via the parent column, fixed only in #64209 — this September); "share points at deleted file, skipping" losing shares mid-transfer (#25693); silent failure on insufficient target quota (#31868); files invisible in the web UI after transfer on object storage (#48463). Beyond the tool: deleting a shared file leaves orphaned link rows serving an error page (#46369), and NC18 once cascade-wiped the whole oc_share table off one federated-share removal (forum).

3. What users say

The recurring thread shape is "we migrated auth / offboarded someone / cleaned up accounts, and the shares vanished" — e.g. deleted users and shared links are gone, recover from backup? (local→AD migration deleted every share those users had made). Nobody is warned at deletion time; discovery is downstream, by the people the shares were serving.

4. What this means for Cairn

Two failure families to avoid: cascade surprise (Nextcloud/ownCloud — deletion silently destroys shares, sometimes other people's) and undead orphans (oCIS — content survives that nobody, admin included, can govern). Both stem from the same omission: no preview, no deliberate rule, no notification.

Cairn has one constraint none of them have, and it decides half the question for us: reads resolve through the owner's key scope. On keycloak-profile custody, a vanished directory account takes its key with it — a departed owner's grants and strong links cannot keep serving through the normal path, only through a deliberate recovery-identity code path. So "keep everything working like nothing happened" is not a lazy default available to us; it would have to be built on purpose. Good.

Options

A — Freeze, surface, decide (recommended). When the creator no longer resolves — deleted locally or vanished from the directory — everything they created stops serving: grants stop resolving (already the fail-closed rule for addressees; apply it to creators symmetrically) and public links answer 404 exactly like an expired link. Nothing is mutated; files stay where they are. The admin gets a Departed owners listing (shares grouped by dangling CreatedBy) with per-share revoke and, if the content must stay shared, the admin re-shares it from an account that exists — an explicit, audited act by an accountable person, not a silent transfer. Local deletion gets a confirmation dialog listing what will stop serving (the preview every competitor lacks).

B — Cascade revoke on local deletion + freeze for directory accounts. Deliberate delete-time revocation (audited) for local accounts, freeze for IdP vanishing (which has no deletion event to hook). Two rules where A has one; the split invites the Nextcloud class of surprise on the local path, and the end state is the same bytes-wise. Only worth it if "deleted = record gone" matters more than a uniform rule.

C — Transfer-ownership tooling. The competitors' answer, with their documented bug tail, and in Cairn a transfer is a cross-scope move, which re-encrypts — colliding with holds (#138-class), versions (#541) and quota. Not for v0.7; if real demand appears, build it later on top of A, as an explicit admin action.

The consequence said out loud, per the brief: under A, the day someone leaves, their public links die. That is the point — a departed employee's files should not keep flowing outward under nobody's accountability — but it will surprise whoever was consuming the link, so the handbook states it and the Departed-owners panel makes the re-share one click for the admin. The alternative (links that keep serving via the recovery key) means the server routinely decrypts with the break-glass identity — a much worse sentence to have to write down.

Implementation size if A is chosen: small, as the issue hoped — a creator-liveness check where grants resolve and where /s/ serves (needs a "known principals" oracle: local store + seats seen-list + live IdP answer, fail-frozen on IdP outage so an outage doesn't mass-kill links — freeze is temporary and self-heals when the directory returns); the Departed-owners admin listing; the local-delete confirmation preview; audit verbs; handbook sentences. Interacts cleanly with #595 (same governance neighborhood) and gives #569's addressee half its mirror.

Your call — A, B, or C (or A with amendments).

## Research done — four products, two failure families, options + recommendation below ### 1. What each product actually does on user deletion **Nextcloud** — deleting a user deletes their files **and every share they created**, public links included. The escape hatch is `occ files:transfer-ownership`, run manually *before* deletion; it moves files plus outgoing shares into a subfolder of the target user. Trash is not transferred, incoming shares are not moved by default. There is no automatic step, no warning in the deletion flow, and admins who deleted first and read the manual second show up in the forums asking how to restore from backup. **ownCloud classic** — same model; the admin manual warns outright that deleting a user destroys everything they shared. There is a `cleanup-orphaned-shares` maintenance command, which tells you orphans happen often enough to need a broom. **oCIS** — the opposite failure: deletion **orphans** content instead of destroying it. A deleted user's personal and project spaces keep existing ([#4195](https://github.com/owncloud/ocis/issues/4195)), the admin cannot access or delete them ([#4194](https://github.com/owncloud/ocis/issues/4194), [#4196](https://github.com/owncloud/ocis/issues/4196)), and removing a space's only Manager leaves the space unmanageable by anyone including admins ([#4105](https://github.com/owncloud/ocis/issues/4105), [#7265](https://github.com/owncloud/ocis/issues/7265)). Undead data nobody controls — on the product whose whole redesign was supposed to fix this class. **Seafile** — library-centric: deleting a library cancels all shares in it (restorable from admin trash); user deletion is handled by admins transferring libraries, with its own bug tail — ownership transfer drops assigned user/group permissions ([forum 16706](https://forum.seafile.com/t/owner-change-removes-assigned-user-and-group-permissions-on-library/16706)), transferring groups from a deleted user misbehaves ([forum 14484](https://forum.seafile.com/t/issue-when-transferring-groups-from-deleted-user-to-new-owner/14484)). ### 2. The pitfalls they hit The transfer tool is where Nextcloud bleeds: public shares silently deleted on transfer ([#60223](https://github.com/nextcloud/server/issues/60223), root cause a cascade-delete via the `parent` column, fixed only in [#64209](https://github.com/nextcloud/server/pull/64209) — this September); "share points at deleted file, skipping" losing shares mid-transfer ([#25693](https://github.com/nextcloud/server/issues/25693)); silent failure on insufficient target quota ([#31868](https://github.com/nextcloud/server/issues/31868)); files invisible in the web UI after transfer on object storage ([#48463](https://github.com/nextcloud/server/issues/48463)). Beyond the tool: deleting a shared file leaves orphaned link rows serving an error page ([#46369](https://github.com/nextcloud/server/issues/46369)), and NC18 once cascade-wiped the whole `oc_share` table off one federated-share removal ([forum](https://help.nextcloud.com/t/nc-18-0-attention-possible-loss-of-all-your-shares-better-implement-fix-now/70145)). ### 3. What users say The recurring thread shape is *"we migrated auth / offboarded someone / cleaned up accounts, and the shares vanished"* — e.g. [deleted users and shared links are gone, recover from backup?](https://help.nextcloud.com/t/deleted-users-and-shared-links-are-gone-process-to-recover-from-backup/159328) (local→AD migration deleted every share those users had made). Nobody is warned at deletion time; discovery is downstream, by the people the shares were serving. ### 4. What this means for Cairn Two failure families to avoid: **cascade surprise** (Nextcloud/ownCloud — deletion silently destroys shares, sometimes other people's) and **undead orphans** (oCIS — content survives that nobody, admin included, can govern). Both stem from the same omission: no preview, no deliberate rule, no notification. Cairn has one constraint none of them have, and it decides half the question for us: **reads resolve through the owner's key scope**. On `keycloak-profile` custody, a vanished directory account takes its key with it — a departed owner's grants and strong links *cannot* keep serving through the normal path, only through a deliberate recovery-identity code path. So "keep everything working like nothing happened" is not a lazy default available to us; it would have to be built on purpose. Good. ## Options **A — Freeze, surface, decide (recommended).** When the creator no longer resolves — deleted locally or vanished from the directory — everything they created **stops serving**: grants stop resolving (already the fail-closed rule for addressees; apply it to creators symmetrically) and public links answer 404 exactly like an expired link. Nothing is mutated; files stay where they are. The admin gets a **Departed owners** listing (shares grouped by dangling `CreatedBy`) with per-share *revoke* and, if the content must stay shared, the admin re-shares it from an account that exists — an explicit, audited act by an accountable person, not a silent transfer. Local deletion gets a confirmation dialog listing what will stop serving (the preview every competitor lacks). **B — Cascade revoke on local deletion + freeze for directory accounts.** Deliberate delete-time revocation (audited) for local accounts, freeze for IdP vanishing (which has no deletion event to hook). Two rules where A has one; the split invites the Nextcloud class of surprise on the local path, and the end state is the same bytes-wise. Only worth it if "deleted = record gone" matters more than a uniform rule. **C — Transfer-ownership tooling.** The competitors' answer, with their documented bug tail, and in Cairn a transfer is a cross-scope **move, which re-encrypts** — colliding with holds (#138-class), versions (#541) and quota. Not for v0.7; if real demand appears, build it later on top of A, as an explicit admin action. **The consequence said out loud, per the brief:** under A, the day someone leaves, their public links die. That is the point — a departed employee's files should not keep flowing outward under nobody's accountability — but it will surprise whoever was consuming the link, so the handbook states it and the Departed-owners panel makes the re-share one click for the admin. The alternative (links that keep serving via the recovery key) means the server routinely decrypts with the break-glass identity — a much worse sentence to have to write down. **Implementation size if A is chosen:** small, as the issue hoped — a creator-liveness check where grants resolve and where `/s/` serves (needs a "known principals" oracle: local store + seats seen-list + live IdP answer, fail-frozen on IdP outage so an outage doesn't mass-kill links — freeze is temporary and self-heals when the directory returns); the Departed-owners admin listing; the local-delete confirmation preview; audit verbs; handbook sentences. Interacts cleanly with #595 (same governance neighborhood) and gives #569's addressee half its mirror. Your call — A, B, or C (or A with amendments).
Author
Owner

Decision (Nikola, 2026-09-18): Option A as the automatic rule, plus Option C built deliberately on top of it — from the start, not later.

Rationale, in his words: transfer tooling is already a market demand (every competitor's admins reach for it, and Nextcloud only just fixed its worst transfer bug); "showing up to a market with the solution in hand already is better than showing up with a marginally better solution."

So the shipped behaviour will be:

  1. Freeze + surface (A) as what happens by default: a creator who no longer resolves means their grants stop resolving and their public links 404, nothing mutated, self-healing on IdP outage recovery. Admin gets a Departed owners listing; local deletion gets a preview dialog of what will stop serving.
  2. Transfer ownership (C) as an explicit, audited admin action available from day one — from the Departed-owners listing and as a pre-deletion offer in the delete dialog.

Cairn-specific design lines for the transfer, so we do not import Nextcloud's bug tail:

  • Build it on the existing move chokepoint. Cross-scope moves already re-encrypt to the destination scope (encrypt/group.go), already refuse under legal hold, already hit quota and audit. A transfer is "move /home/<gone>/… into /home/<target>/…" plus rewriting the affected share records — not a parallel copy machine.
  • Grants follow the file: re-pointed to the new backend path, CreatedBy becomes the target admin-chosen owner, addressees untouched. Fail loudly on any record that cannot be re-pointed; never skip-and-continue (the "share points at deleted file, skipping" lesson).
  • Public links break on transfer, by design, and the preview says so. Links bind to a path and renaming breaks them deliberately (sharing.md); a transfer is a move. This diverges from Nextcloud's newest behaviour (they now preserve link shares) — flagged for veto: if links surviving transfer matters commercially, the alternative is re-pointing the share record's path, which our token-addressed store can do, at the cost of the "links bind to a path" invariant.
  • Quota on the target is checked before anything moves — a transfer that would not fit refuses up front (the silent-quota-failure lesson).

Implementation issue follows with the full brief; the Departed-owners panel and the transfer flow get a mockup before any UI is built.

**Decision (Nikola, 2026-09-18): Option A as the automatic rule, plus Option C built deliberately on top of it — from the start, not later.** Rationale, in his words: transfer tooling is already a market demand (every competitor's admins reach for it, and Nextcloud only just fixed its worst transfer bug); *"showing up to a market with the solution in hand already is better than showing up with a marginally better solution."* So the shipped behaviour will be: 1. **Freeze + surface (A)** as what happens by default: a creator who no longer resolves means their grants stop resolving and their public links 404, nothing mutated, self-healing on IdP outage recovery. Admin gets a **Departed owners** listing; local deletion gets a preview dialog of what will stop serving. 2. **Transfer ownership (C)** as an explicit, audited admin action available from day one — from the Departed-owners listing and as a pre-deletion offer in the delete dialog. **Cairn-specific design lines for the transfer, so we do not import Nextcloud's bug tail:** - Build it on the existing **move chokepoint**. Cross-scope moves already re-encrypt to the destination scope (`encrypt/group.go`), already refuse under legal hold, already hit quota and audit. A transfer is "move `/home/<gone>/…` into `/home/<target>/…`" plus rewriting the affected share records — not a parallel copy machine. - **Grants follow the file**: re-pointed to the new backend path, `CreatedBy` becomes the target admin-chosen owner, addressees untouched. Fail loudly on any record that cannot be re-pointed; never skip-and-continue (the "share points at deleted file, skipping" lesson). - **Public links break on transfer, by design, and the preview says so.** Links bind to a path and renaming breaks them deliberately (sharing.md); a transfer is a move. This diverges from Nextcloud's newest behaviour (they now preserve link shares) — flagged for veto: if links surviving transfer matters commercially, the alternative is re-pointing the share record's path, which our token-addressed store can do, at the cost of the "links bind to a path" invariant. - Quota on the target is checked **before** anything moves — a transfer that would not fit refuses up front (the silent-quota-failure lesson). Implementation issue follows with the full brief; the Departed-owners panel and the transfer flow get a mockup before any UI is built.
Cordy closed this issue 2026-09-22 20:00:32 +00:00
Author
Owner

Closing: this issue's scope (research + recorded decision) is complete — Option A (freeze + surface) with C (transfer) built on top, decided 2026-09-18 above — and the implementation shipped and closed as #597. Live UI verification of the departed-owner flow is pending as a dogfood exercise (delete one of the sharer1/sharer2 test accounts, confirm the frozen rows, Departed-owners listing, delete preview and transfer); any findings there get filed as fresh issues against the shipped behaviour.

Closing: this issue's scope (research + recorded decision) is complete — Option A (freeze + surface) with C (transfer) built on top, decided 2026-09-18 above — and the implementation shipped and closed as #597. Live UI verification of the departed-owner flow is pending as a dogfood exercise (delete one of the sharer1/sharer2 test accounts, confirm the frozen rows, Departed-owners listing, delete preview and transfer); any findings there get filed as fresh issues against the shipped behaviour.
Sign in to join this conversation.
No labels
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#569
No description provided.