Departed owners: freeze, surface, transfer (#569 implementation) #597
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#597
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the #569 decision (2026-09-18): freeze + surface as the automatic rule, transfer ownership as an explicit admin action from day one. The full rationale, competitor evidence and design lines live on #569; this issue is the build brief.
1. Creator liveness (the oracle)
One question, answered in one place: does this username still resolve to a principal? Sources: the local-users store, the seats seen-list, and — where an IdP admin client exists — a live directory answer on the same 60s cache live group resolution already uses (#96). Fail frozen, not dead: an IdP outage makes the answer "unknown", which freezes affected shares for the outage's duration and self-heals — it must never mass-revoke.
2. Freeze (fail-closed serving)
CreatedByno longer resolves stops resolving for its addressees — symmetric with the addressee half already shipped in #570. The recipient's row says why rather than vanishing (the dead-row pattern from the #571 mockup).3. Surface (admin)
Departed owners listing under Administration › Sharing: shares grouped by dangling creator, each with revoke, plus the transfer action below. Lazy-loaded. Mockup first (house rules) — rides the next mockup batch.
Local-delete preview: deleting a local account lists what will stop serving (n grants, n public links) before the confirm — the preview every competitor lacks.
4. Transfer (the explicit act)
From the Departed-owners listing and as an offer inside the local-delete dialog. Per the #569 decision comment:
/home/<gone>/…moves into/home/<target>/…, inheriting re-encryption to the destination scope, legal-hold refusal, quota enforcement and audit. No parallel copy machinery.CreatedByset to the new owner, addressees untouched. Any record that cannot be re-pointed fails the transfer loudly — never skip-and-continue.owner-transfer, plusshare-freeze/share-unfreezeif freeze events are worth recording — decide during TDD, registry will force literals either way.5. Docs
sharing.md: what happens when an owner departs (freeze, the admin path, the transfer). local-accounts.md: the delete preview. Handbook sentences land with the behaviour, same-PR, per the #588 pattern.
Build order
Backend TDD first (oracle → freeze at both serve points → transfer on the move chokepoint), UI after the mockup is approved. Slots after the current Phase-1 batch (#572, Used-by, #577).
Backend shipped in v0.6.227 (PR #627, merge
d577c06, red-green run on fix-597). Both dogfoods verified running the new image.What landed:
internal/liveness.Oracle— one place answers "does this username still resolve": local store first; in local auth mode absence there means departed; in oidc mode the IdP admin client answers on a short cache (60s positive / 15s negative), fail-frozen. Empty creator always reads alive so legacy records never freeze.FileStore.RepointGrants— all-or-nothing grant re-pointing (validate everything before writing, rollback on save failure). Public links deliberately untouched: they bind to paths and break with the move.GET /api/v1/admin/owners/departed(grouped listing with counts and sample paths),GET /api/v1/admin/owners/preview?user=(what a local-account delete will freeze),POST /api/v1/admin/owners/transfer— dry-run repoint, then/home/<from>→/home/<to>/<from>through the unscoped move chokepoint (re-encryption to the destination scope, legal holds, quota and the versions chain all inherit), then apply repoint, audit verbowner-transfer, activitymove. Quota pre-checked via the quota driver; guards: 409 living source, 422 dead target, 404 no home tree, 409 destination exists, 409 quota.Deliberate choices, for the record: the seats seen-list is not used as a liveness source (it cannot distinguish departed from merely inactive); freeze events are not audited (the freeze is computed state, not a mutation); after a transfer the departed user's public links remain as broken records, visible in the departed listing for revocation.
Still open on this issue's scope: the admin UI slice — departed-owners listing under Administration › Sharing plus the local-delete preview dialog — which follows mockup-first per house rules. Leaving the issue open for that.
Admin UI shipped in v0.6.228 (PR #628, merge
a918024, red-green on fix-597-ui, commit517d1ae). Both dogfoods verified running the new image. With the backend (v0.6.227) this completes the issue.Per the approved rev-1 mockup, with the three open decisions resolved as: revoke-all gets its own atomic endpoint; the transfer target is a picker; unverifiable instances get honest empty text.
What landed:
POST /api/v1/admin/owners/revoke— atomicFileStore.DeleteByCreator(all-or-nothing with rollback), 409 while the account still resolves, audit verbowner-revoke, response {revoked, grants, links}. The confirm carries the counts and the permanence warning./admin/owners/preview); silent when the account shared nothing.verifiable+directoryflags. 24 new i18n keys ×4 (en/de/fr/it). New pure blockpure:departedwith a 14-assert web suite.One placement note vs. the mockup: the real Administration › Sharing page is tabbed (Policy / Active links), so the section landed as a third tab rather than "below the switches" — same page, cleaner fit. Revoke-all rides the standard confirm dialog rather than a bespoke one; the message carries the same facts.
Closing. Verification pass for the dogfood: delete a throwaway local account that owns a share, watch it appear under Departed owners, transfer it, confirm the files land under the new owner's home with the grant intact.