From dd3927a661273734493f65a593755b95aecf5f09 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 15 Aug 2026 17:23:10 +0200 Subject: feat(groups): remove a member, and keep gigabytes out of the tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Removing a member.** The owner can do it from the Members tab, and it is two halves in the order that fails safe: the node stops serving the group key first (an operator-signed request, so a paired browser only), then the hub drops the membership row. The other order would leave someone able to reach a node that still serves them. It is a membership, not an account. The user row is never written: their other groups, their files and their pinned identity survive, because one group's owner must not be able to erase someone from the hub. It is also per group — a node hosting two loses them from one — and it does not take back the key they already unwrapped, which is what rotating the GEK is for. The confirmation and the panel both say so. **Downloads and streaming through the disk, in both browsers.** The audit this started as found two ways to put gigabytes in a tab. Firefox and Safari have no File System Access API, so every download there was collected in memory. A service worker fixes it: the page keeps the writable half of a transferred stream, the worker answers a made-up URL with the readable half and a Content-Disposition header, and the browser writes it to disk as it arrives, with real backpressure. The worker caches nothing and falls through on every request that is not one of these downloads. A zip announces no Content-Length, since the archive is larger than the files in it and a length we miss truncates the file. Video was worse and affected both browsers. The node pushed ffmpeg's whole output as fast as it was produced while the player consumed a segment at a time, so the queue held the film — and appending all of it hit the SourceBuffer's cap, where the handler logged the error and dropped the segment, leaving a hole in the middle of the film with nothing to show for it. Streaming is credit-based now, 24 segments of 256 KB in flight, verified against the live node: three credits, three segments, then silence until more are granted. The player evicts what is more than a minute behind the playhead and retries a refused segment rather than dropping it. Co-Authored-By: Claude Opus 5 --- docs/USERGUIDE.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index fea02fd..7242dc2 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -548,10 +548,13 @@ one and something else in another before choosing an action. - **Ask every time** opens a Save As dialog per file, which is right for one file and wrong for a selection of twenty. -With no folder chosen, automatic still does not put a dialog in your way: the file -goes to the browser's own download folder. The exception is a download over 512 MB, -which cannot be held in memory to get there — that one asks where to put it, and -choosing a folder in Settings stops it asking again. +With no folder chosen, automatic still does not put a dialog in your way, and it +still does not hold the file in memory: a service worker hands the browser a +stream, which it writes to its own download folder as the bytes arrive. That is +how this works in Firefox and Safari, which have no way to open a file for writing +from a page. If even that is unavailable, a download under 512 MB is collected in +memory and handed over; a larger one asks where to put it, because a tab does not +survive a multi-gigabyte blob. A finished download offers **Open** in the transfers widget when it went into a folder you granted: the file is handed to a new tab and the browser decides what @@ -599,6 +602,30 @@ disk, so a 40 GB folder costs 40 GB of disk and a few megabytes of memory. browser says so, with the size, before starting. Use Chrome or Edge for a large one. +### Removing a member + +The group's owner can remove someone from the Members tab. It does two things, in +the order that fails safe: + +1. **The node stops serving them the group key** — an operator-signed request, so + it works only from a paired browser (§3). This is the half that matters. +2. **The hub drops their membership**, which is what stops them reaching the node + through signaling at all. + +What it does **not** do: + +- It does not delete their account. Their other groups, their files and their + identity are untouched — one group's owner cannot erase someone from the hub. +- It does not make the node forget them. The pinned key stays, so they can be + admitted again without a new pairing code; `meshbay-node member unpin` forgets. +- It does not take back the key they already hold. Anyone who has connected has + unwrapped the current GEK, and no protocol reaches into their browser to remove + it. Rotate it with `meshbay-node gek-init --group ` if that matters — + members still in the group pick the new one up on their next connection. + +Removal is per group: on a node hosting several, someone removed from one keeps +the others. + ### Deleting a directory **⋮ → Delete folder**, for the node operator, from a paired browser (§3). The @@ -707,6 +734,20 @@ Each `stream_data` segment is decrypted with the chunk key for its `segment_inde appended to a `SourceBuffer`. The web client does this in `static/app.js` (`VideoPlayer`); ffmpeg must be installed on the node for transcoding. +**Flow control.** The client says how many segments it can take — `stream_req` +carries a credit count — and the node sends no more than that until `stream_more` +grants more. Without it the node hands ffmpeg's entire output to the channel as +fast as it is produced, and the browser holds a whole film in memory while the +player consumes it a segment at a time. Segments are 256 KB and the web client +keeps 24 outstanding, so roughly 6 MB is in flight whatever the film's length. A +client that sends no credit count gets the old unpaced behaviour. + +**The player drops what has been watched.** A SourceBuffer is not a file: browsers +cap it and refuse the append that goes past, so anything more than a minute behind +the playhead is evicted. A segment refused for want of room is retried rather than +dropped — dropping it leaves a hole in the middle of the film and no error +anywhere. + --- ## 8. Security Model -- cgit v1.2.3