summaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md19
1 files changed, 18 insertions, 1 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 892b747..0211d3c 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -2124,6 +2124,24 @@ MSE string) fed to a source buffer, with the node holding one slot per viewer.
Autoplay is blocked on a phone more often than not, so that is the ordinary
first state of every playback. Pausing does not shrink it: the lead already
paid for is the viewer's.
+- **A reconnection carries the read-ahead across; it does not spend it.** The
+ old stream dies with the connection, so something must be asked for again,
+ and the question is *where*. Restarting at the playhead goes through the
+ reinit that empties the source buffer — which a seek needs and a reconnection
+ does not: the viewer navigated nowhere, the film kept playing out of the
+ buffer for the whole outage, and emptying it at the moment the link returns
+ is what finally stops the picture. So where there is read-ahead worth
+ keeping, the stream carries on from the *end* of the buffer and what is there
+ is left alone. **The two modes are not interchangeable** — resuming keeps the
+ buffer and must not move the playhead, seeking discards it and must — and the
+ choice is one pure function so it can be tested by being run. Below a few
+ seconds of read-ahead, and for a stream that already ended, the seek is still
+ what happens: there is nothing to save, and it is the proven path. The node
+ lands on a keyframe at or before what is asked for, so the new material
+ overlaps the tail of the old and the coded frame processing replaces it; a
+ start that somehow fell *past* the buffer would leave a gap, which is a
+ permanent silent stall, so that case falls back to the reinit rather than
+ trusting the reasoning.
- **An append refused for quota is not retried until something frees room.** It
will be refused again for as long as the answer has not changed, and the
pipeline's clock calls the append path several times a second: the retry
@@ -3170,7 +3188,6 @@ process runs it — `systemctl --user` on Linux, Task Scheduler on Windows.
| **QUIC** | Off by default, and **not at parity**: it serves the index and file chunks with no transfer lease, no leaseless ceiling and no root-availability check, does its file I/O on the event loop, and returns exception text to the peer (**L3**). No client speaks it. Either it comes to parity or it goes; until then §5.1's "chat is the only gap" is the one sentence here that overstates the code |
| **The relay registry** | **Closed in the code**: `relay.RELAYS_ENABLED` is False and every `/v1/relays` route answers 503, as federation does. Nothing in the tree calls them, node or client, and §11.1 measured two ISPs with no TURN relay needed. Kept code that nothing calls is what **L7** says not to keep; it stays only as the proof-of-possession design (**AV6**) until a node needs a relay or it is deleted |
| **A very high bitrate wedges the player against a small buffer ceiling** | Where even the *floor* read-ahead does not fit — ninety seconds plus the minute kept behind, at the file's bitrate, above what the engine will hold — the film stalls: measured on the harness at 9.3 Mbit/s against a 100 MB ceiling, 100.8 s of film played in 900 s of wall clock. **Predates the byte budget and is unchanged by it**, to the tenth of a second; what the budget did change there is the refusal count, 1560 → 2. The fix is not a bound at all, it is a second stage of buffer outside the SourceBuffer, which means gating the append path — the riskiest change in this area and not one to make alongside another |
-| **A reconnection throws the read-ahead away** | `addReconnectListener` resumes with a seek to the playhead, and `reinitAt` empties the SourceBuffer, so a transport drop costs every second of buffer that was the point of building it. Harmless while the read-ahead was ninety seconds and the buffer was empty by the time the link returned; with a budget of minutes it is the thing that stops the budget paying. Resuming at the *end* of what is buffered instead is the fix, and it touches the seek path, which is why it is here and not in the same commit |
| **The reconnect backoff only wakes on `visibilitychange`** | So a tab that stays visible through an outage — which is what a screen wake lock guarantees while a film is playing — waits out the full backoff, up to 30 s, after the network is already back. Nothing listens for `online` |
| **Per-device revocation has no CLI** | A device is revoked over MNP (`roster.revoke_device`), from a device the node has already pinned. On a headless node the operator's only lever is `member unpin`, which removes **every** device of that account — so the per-device control the roster is built around is reachable from an interface and from nowhere else. §6.7 listed a `meshbay-node member device list\|revoke` verb that was never written, and that listing is how this was found: `USERGUIDE.md` was the first document written by reading the CLI rather than this specification, and the verb it copied out did not run |
| **Migrations run on SQLite only** | The chain reaches head and agrees with the models there (§12), which is not where it ships. **The exposure is one revision deep, not the whole chain**: every revision behind the first packaged release was development that no installation ever ran, so nothing replays them on PostgreSQL. What is unguarded is the *next* migration — a default, an index type or a constraint PostgreSQL refuses reaches a deploy without the suite saying so |