Unshare a shared folder: owner control, server tells members #10

Open
opened 2026-09-14 20:58:02 +01:00 by cruelacid · 2 comments
Owner

After sharing a folder there is no way to unshare it; removing every member leaves the row. Fix: any folder owner may DELETE /api/folders/:id (was creator only); the shard sends a FolderGone frame to every subscriber and drops the folder's documents; the plugin offers Unshare… with a confirm modal on the organisation page, and a member's vault disconnects the folder, keeps the notes and mapping, shows a notice and lists it under No longer shared. Checkpoint: server + plugin unit tests, e2e (owner unshares, guest keeps notes, page shows No longer shared), mutation checks, shard then plugin deployed, live check from Test Vault 2 to Mind Palace.

After sharing a folder there is no way to unshare it; removing every member leaves the row. Fix: any folder owner may DELETE /api/folders/:id (was creator only); the shard sends a FolderGone frame to every subscriber and drops the folder's documents; the plugin offers Unshare… with a confirm modal on the organisation page, and a member's vault disconnects the folder, keeps the notes and mapping, shows a notice and lists it under No longer shared. Checkpoint: server + plugin unit tests, e2e (owner unshares, guest keeps notes, page shows No longer shared), mutation checks, shard then plugin deployed, live check from Test Vault 2 to Mind Palace.
Author
Owner

Checkpoint, 14 Sep 2026 — 33cb1a4 (shard + protocol) and ba82740 (plugin).

Server: any folder owner may DELETE /api/folders/:id, decided inside the transaction so a demotion cannot race it (was creator or instance admin only). New S->C MessageType.FolderGone (18) naming the bare folder id — which is what makes shard-before-plugin safe, since an older plugin looks the name up among its subscriptions, finds none and returns. evictFolder tells each subscribed socket once, drops those subscriptions and the in-memory documents; called only when the delete succeeded, never in the route's finally.

Plugin: Unshare… on owned folders on the organisation page, behind a confirmation naming what goes (server copy, history, attachments, for everyone) and what stays (every vault's notes). Local state dropped only after the server agrees. On receiving FolderGone a member's vault disconnects the folder, keeps the notes, the mapping and the keys, shows a notice and lists it under No longer shared; refreshSync no longer reconnects it, which had left the status bar claiming a dead folder synced.

Also found and fixed while testing: an empty folder listing returned early and never drew the orphan rows, so unsharing the last folder made its mapping invisible — still routing files with nothing on screen to unmap.

Tests: server unshare.test.ts (owner deletes, editor 404, one frame to each subscriber, none to a bystander, none at all when refused, documents dropped) + storage-hygiene owner/editor cases; plugin unshare-folder, folder-gone, provider-folder-gone, folder-sections empty-listing. E2e: owner unshares from the pane, eu1 row and documents gone, guest told over the socket, both vaults' notes byte-identical, guest's page shows No longer shared with Unmap. 11 mutation checks all caught, including the e2e one (shard not telling the member → the guest is never told).

Full multi-vault suite green: 56 passed, 2 skipped, no failures. Deployed eu1 ba82740; plugin installed in both vaults (sha256 a5445cf3…).

Left for the user: from Test Vault 2 unshare a folder Mind Palace maps, and check the notice, the orphan row and that the notes are untouched.

Checkpoint, 14 Sep 2026 — 33cb1a4 (shard + protocol) and ba82740 (plugin). Server: any folder owner may DELETE /api/folders/:id, decided inside the transaction so a demotion cannot race it (was creator or instance admin only). New S->C MessageType.FolderGone (18) naming the bare folder id — which is what makes shard-before-plugin safe, since an older plugin looks the name up among its subscriptions, finds none and returns. evictFolder tells each subscribed socket once, drops those subscriptions and the in-memory documents; called only when the delete succeeded, never in the route's finally. Plugin: Unshare… on owned folders on the organisation page, behind a confirmation naming what goes (server copy, history, attachments, for everyone) and what stays (every vault's notes). Local state dropped only after the server agrees. On receiving FolderGone a member's vault disconnects the folder, keeps the notes, the mapping and the keys, shows a notice and lists it under No longer shared; refreshSync no longer reconnects it, which had left the status bar claiming a dead folder synced. Also found and fixed while testing: an empty folder listing returned early and never drew the orphan rows, so unsharing the last folder made its mapping invisible — still routing files with nothing on screen to unmap. Tests: server unshare.test.ts (owner deletes, editor 404, one frame to each subscriber, none to a bystander, none at all when refused, documents dropped) + storage-hygiene owner/editor cases; plugin unshare-folder, folder-gone, provider-folder-gone, folder-sections empty-listing. E2e: owner unshares from the pane, eu1 row and documents gone, guest told over the socket, both vaults' notes byte-identical, guest's page shows No longer shared with Unmap. 11 mutation checks all caught, including the e2e one (shard not telling the member → the guest is never told). Full multi-vault suite green: 56 passed, 2 skipped, no failures. Deployed eu1 ba82740; plugin installed in both vaults (sha256 a5445cf3…). Left for the user: from Test Vault 2 unshare a folder Mind Palace maps, and check the notice, the orphan row and that the notes are untouched.
Author
Owner

Follow-up, 14 Sep 2026 — 91b2bc6, from the report of two 'test' folders with only one Unshare button.

Not a duplicated row: two real folders (cb0820cf created 16:57:10, 3990bcb2 created 17:01:08), the same vault folder shared twice before a second share of a mapped path was refused. The missing button had a worse cause.

Adding a member is an upsert, so naming an existing member changes their role, and nothing stopped that taking the last owner away — though removing the last owner has always been refused. cb0820cf's creator had added their own address with the role box at its default and was demoted to editor, leaving the folder with no owner at all: no way to grant access, and no Unshare button, which asked for the owner role. Fixed: the add path refuses that demotion with the same 409 as removal.

The button also asked for less than the server allows. The server has always let a folder's creator delete it, so the pane now offers unsharing to an owner and to the creator, through one shared predicate (mayUnshare) so the two sides cannot drift.

Tests: server (demotion of the last owner refused, allowed once a second owner exists), plugin (owner, creator by user id, self-hosted creator by username, nobody else), e2e (a member who is neither sees only Unmap). Four mutation checks caught. Unit 354 plugin / 334 server green; full multi-vault suite 56 passed, 2 skipped, no failures. Deployed eu1 91b2bc6; plugin installed in both vaults (sha256 8efb425b…).

The stranded folder cb0820cf is untouched on the server: with this build its creator can unshare it from the pane, which is the cleanest way to clear it.

Follow-up, 14 Sep 2026 — 91b2bc6, from the report of two 'test' folders with only one Unshare button. Not a duplicated row: two real folders (cb0820cf created 16:57:10, 3990bcb2 created 17:01:08), the same vault folder shared twice before a second share of a mapped path was refused. The missing button had a worse cause. Adding a member is an upsert, so naming an existing member changes their role, and nothing stopped that taking the last owner away — though removing the last owner has always been refused. cb0820cf's creator had added their own address with the role box at its default and was demoted to editor, leaving the folder with no owner at all: no way to grant access, and no Unshare button, which asked for the owner role. Fixed: the add path refuses that demotion with the same 409 as removal. The button also asked for less than the server allows. The server has always let a folder's creator delete it, so the pane now offers unsharing to an owner and to the creator, through one shared predicate (mayUnshare) so the two sides cannot drift. Tests: server (demotion of the last owner refused, allowed once a second owner exists), plugin (owner, creator by user id, self-hosted creator by username, nobody else), e2e (a member who is neither sees only Unmap). Four mutation checks caught. Unit 354 plugin / 334 server green; full multi-vault suite 56 passed, 2 skipped, no failures. Deployed eu1 91b2bc6; plugin installed in both vaults (sha256 8efb425b…). The stranded folder cb0820cf is untouched on the server: with this build its creator can unshare it from the pane, which is the cleanest way to clear it.
Sign in to join this conversation.
No labels
No milestone
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
Nectenda/nectenda#10
No description provided.