aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py72
1 files changed, 38 insertions, 34 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 3bb0df7..4540f2f 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -174,7 +174,8 @@ USER_BLOB_ACCOUNT_MAX = 8 * 1024 * 1024
_USER_BLOB_KIND_RE = re.compile(
r"^(playlists|playlist:[A-Za-z0-9_-]{1,64})$")
-# Chat link-preview results, kept in memory only (draft-v6 §2.7: the node
+# Chat link-preview results, kept in memory only (docs/MESHBAY_DESIGN.md §6.5:
+# the node
# produces enrichment on demand and keeps nothing durable — the asking device
# caches). Bounded and time-limited so a busy group cannot grow it without end
# and a page that changed its card is picked up within the hour.
@@ -998,7 +999,7 @@ class WebRTCPeerSession:
self._ctx.get("daemon_state", {}).get("tmdb_token_customized", False)),
"tmdb_language": str(
self._ctx.get("daemon_state", {}).get("tmdb_language") or ""),
- # Music app (docs/musicbay.md §6) — same shape as the TMDB
+ # Music app (docs/MESHBAY_DESIGN.md §9.8) — same shape as the TMDB
# fields above. No language field: MusicBrainz search doesn't
# take one the way TMDB does.
"musicbrainz_enabled": bool(self._group_ctx().get("musicbrainz_enabled", True)),
@@ -1174,7 +1175,7 @@ class WebRTCPeerSession:
}
# The recovery-wrapped copy (MNP 0.14) rides along when present, so a
# client holding the recovery key can re-wrap it under a new
- # passphrase — docs/auth-confirm.md §4.5.
+ # passphrase — docs/MESHBAY_DESIGN.md §3.6.
if kp.get("bundle_enc_recovery"):
resp["bundle_enc_recovery"] = kp["bundle_enc_recovery"]
self._send(resp)
@@ -1558,7 +1559,7 @@ class WebRTCPeerSession:
# A person may hold several devices on one node. The authority admitting a
# new one is a key the node already pinned — never the hub, which has stored
# no user keys since 2026-08-14 and therefore cannot countersign anything.
- # See docs/desktop-client-v1.md §4.
+ # See docs/MESHBAY_DESIGN.md §3.3.
async def _do_device_request(self, msg: dict) -> None:
"""
@@ -1719,7 +1720,7 @@ class WebRTCPeerSession:
#
# This is what lets another member check for themselves that this device
# belongs to an account whose earlier device they have already pinned,
- # instead of taking the node's word (Tier 2, desktop-client-v1.md §4.8).
+ # instead of taking the node's word (Tier 2, docs/MESHBAY_DESIGN.md §3.3).
await roster.pin_identity(
user_id=self._user_id, username=self._username or "",
pk_ed25519=pk_ed_b64, pk_x25519=pk_x_b64, via="device",
@@ -1746,7 +1747,7 @@ class WebRTCPeerSession:
What is checked, in order: the key is a live device *of this account* in
the node's own roster (never a token claim — that is
- `per-node-identity-v1.md`'s rule), the timestamp is fresh, and the
+ `docs/MESHBAY_DESIGN.md` §3.2's rule), the timestamp is fresh, and the
signature verifies over a transcript naming this node, this group and
this connection's nonce. A key that is merely well-formed proves nothing.
@@ -1964,7 +1965,7 @@ class WebRTCPeerSession:
Admission policy for a group, read from the node's own configuration.
Never from the hub: a hub that could declare a group open would be handed
- the key to it (§3.4 of docs/invite-pairing-v1.md).
+ the key to it (docs/MESHBAY_DESIGN.md §3.4).
"""
gctx = (self._ctx.get("groups") or {}).get(group_id) or {}
return gctx.get("join_policy", "invite")
@@ -2290,9 +2291,9 @@ class WebRTCPeerSession:
# include "video" or "music" — both can make outbound third-party
# network calls (TMDB, MusicBrainz) once enabled, so an operator opts a
# group in explicitly rather than getting it for free
- # (docs/mediacenter.md §5.6, docs/musicbay.md §4.4).
- # `helloworld` is the reference implementation (docs/refactor-groups.md
- # §4.1), hidden client-side behind `?dev=1`. It is here because the
+ # (docs/MESHBAY_DESIGN.md §9.7, §9.8).
+ # `helloworld` is the reference implementation (docs/MESHBAY_DESIGN.md
+ # §9.4), hidden client-side behind `?dev=1`. It is here because the
# allow-list is server-side enforcement — a client that names an app this
# node does not know is refused — and an app the node refused could not
# demonstrate anything. This entry and the client's registry line are the
@@ -2363,7 +2364,7 @@ class WebRTCPeerSession:
per-group concern (see _do_tmdb_enabled for the per-group on/off
switch). Signed like the rest: this changes outbound third-party
network traffic the node did not have before the Videos app
- (docs/mediacenter.md §5.5, §8) — an unsigned change would let any
+ (docs/MESHBAY_DESIGN.md §9.7, §6.5) — an unsigned change would let any
member alter egress the operator never agreed to.
"""
token = msg.get("token")
@@ -2746,7 +2747,7 @@ class WebRTCPeerSession:
def _do_musicbrainz_enabled(self, msg: dict) -> None:
"""
Whether MusicBrainz lookups run for this group at all. Per-group
- from the start (docs/musicbay.md §3.2/§6) — signed like
+ from the start (docs/MESHBAY_DESIGN.md §9.8) — signed like
tmdb_enabled: it decides whether this group's members'
Music tab ever makes outbound MusicBrainz traffic.
"""
@@ -3813,13 +3814,13 @@ class WebRTCPeerSession:
self, thumb_hash: str, chunk_index: int, gek: bytes | None,
) -> dict | None:
"""
- docs/mediacenter.md §5.3: a thumbnail is served through the same
+ docs/MESHBAY_DESIGN.md §6.5: a thumbnail is served through the same
chunked file_req path as a real file, resolved against the media
cache instead of the index when the id doesn't match a file.
Sliced by `chunk_index` like a real file's chunks, not just handed
back whole: a thumbnail/poster/cover never approached CHUNK_SIZE so
this used to be equivalent to "only chunk 0 exists", but an audio
- transcode result (docs/musicbay.md, the WMA/Musepack exception) is
+ transcode result (docs/MESHBAY_DESIGN.md §9.8, the WMA/Musepack exception) is
cached in the same media_cache blob store and can be several MB —
genuinely multi-chunk, same as a file read straight off disk.
"""
@@ -3939,7 +3940,7 @@ class WebRTCPeerSession:
async def _fetch_and_cache_poster(media_cache, tmdb_client, poster_path: str | None) -> str | None:
"""
Downloads a TMDB poster/backdrop once, caches it under its own
- blake3 like a video thumbnail (docs/mediacenter.md §5.4), and
+ blake3 like a video thumbnail (docs/MESHBAY_DESIGN.md §9.7), and
returns the hash a client then fetches via the normal file_req/
chunk path (§5.3) — no client ever contacts image.tmdb.org directly.
@@ -3988,7 +3989,7 @@ class WebRTCPeerSession:
async def _do_audio_transcode_request(self, msg: dict) -> None:
"""
- docs/musicbay.md's one exception to "no node-side transcode pool":
+ docs/MESHBAY_DESIGN.md §9.8's one exception to "no node-side transcode pool":
WMA and Musepack tag/cover fine (enrich_audio.py) but decode in no
mainstream browser's <audio> element at all. Transcoded to AAC/M4A
once and cached under its own content hash — same "computed once,
@@ -4195,7 +4196,7 @@ class WebRTCPeerSession:
async def _do_music_meta_request(self, msg: dict) -> None:
"""
- docs/musicbay.md §4.3: MusicBrainz metadata for one track, resolved
+ docs/MESHBAY_DESIGN.md §9.8: MusicBrainz metadata for one track, resolved
from the group's index by its content id. Album-level (release), the
direct analogue of Videos' show-level TMDB caching: one search per
(artist, album) pair serves cover art and canonical naming to every
@@ -4275,7 +4276,7 @@ class WebRTCPeerSession:
async def _do_media_meta_request(self, msg: dict) -> None:
"""
- docs/mediacenter.md §5.4: TMDB metadata for one file, resolved from
+ docs/MESHBAY_DESIGN.md §9.7: TMDB metadata for one file, resolved from
the group's index by its content id (root+relpath the client already
knows from index_sync/index_delta identify the entry; its own `id`
is what actually names one file — never a raw filesystem path off
@@ -4300,7 +4301,7 @@ class WebRTCPeerSession:
media_cache = self._ctx.get("media_cache")
tmdb_client = self._ctx.get("tmdb_client")
- # Per-group, not node-wide (docs/mediacenter.md §5.5, 2026-08-24):
+ # Per-group, not node-wide (docs/MESHBAY_DESIGN.md §9.7, 2026-08-24):
# treated exactly like "no client configured" — same silent, no-error
# degradation, since a member's Videos tab already has to handle "no
# TMDB match" as the ordinary case.
@@ -4409,7 +4410,7 @@ class WebRTCPeerSession:
return
media_cache = self._ctx.get("media_cache")
tmdb_client = self._ctx.get("tmdb_client")
- # Per-group, not node-wide (docs/mediacenter.md §5.5, 2026-08-24) —
+ # Per-group, not node-wide (docs/MESHBAY_DESIGN.md §9.7, 2026-08-24) —
# same silent zero-confidence degradation as "no client configured".
if (media_cache is None or tmdb_client is None
or not self._group_ctx().get("tmdb_enabled", True)):
@@ -4447,7 +4448,7 @@ class WebRTCPeerSession:
async def _do_tmdb_search_request(self, msg: dict) -> None:
"""
Candidate TMDB matches for an operator correcting a wrong automatic
- match (docs/mediacenter.md, §V-whatever this becomes) — a plain
+ match (docs/MESHBAY_DESIGN.md §9.7, §V-whatever this becomes) — a plain
lookup, not a mutation, so unlike `tmdb_override` this needs no
admin authority: any member can see what TMDB itself would offer,
the same as the automatic search already silently does on their
@@ -4460,7 +4461,7 @@ class WebRTCPeerSession:
return
media_cache = self._ctx.get("media_cache")
tmdb_client = self._ctx.get("tmdb_client")
- # Per-group, not node-wide (docs/mediacenter.md §5.5, 2026-08-24) —
+ # Per-group, not node-wide (docs/MESHBAY_DESIGN.md §9.7, 2026-08-24) —
# same silent empty-results degradation as "no client configured":
# a member with TMDB off for this group sees the same "type it in
# yourself" affordance either way, never an error.
@@ -4593,7 +4594,7 @@ class WebRTCPeerSession:
def _do_tmdb_rematch(self, msg: dict) -> None:
"""
An operator dropping one file's cached TMDB match so it re-resolves
- with the current matcher (§10.1/V13) — the one-click alternative to
+ with the current matcher (V13) — the one-click alternative to
the full search-and-pick "Fix match" flow, and reachable without
SSH (`meshbay-node video rematch` clears a whole group). Signed like
`tmdb_override`: `media_cache` is shared node-wide.
@@ -4635,7 +4636,8 @@ class WebRTCPeerSession:
async def _tmdb_search(self, tmdb_client, entry, is_show: bool):
"""
- §3.3's retry ladder — same shape for movies and shows (§10.1/V8).
+ docs/MESHBAY_DESIGN.md §9.7's scored ladder — same shape for movies
+ and shows (V8).
TMDB's own top result is still trusted per query (§3.3's last row —
no local re-ranking of *its* list); what the ladder adds is that it
*scores every candidate query* and keeps the best, instead of
@@ -4681,7 +4683,7 @@ class WebRTCPeerSession:
specific than a punctuation-normalised restatement of `primary`
(an alternative_title, a sequel variant); when it does not and the
primary hit is already decent, the remaining calls are skipped
- (§10.1/V11 — they almost never win and cost a round trip each).
+ (V11 — they almost never win and cost a round trip each).
"""
def _year_of(res: dict) -> int | None:
d = str(res.get("release_date") or res.get("first_air_date") or "")
@@ -5124,7 +5126,8 @@ class WebRTCPeerSession:
async def _do_link_preview_request(self, msg: dict) -> None:
"""
- Unfurl a URL a member pasted into chat (draft-v6 §2.7 enrichment rule:
+ Unfurl a URL a member pasted into chat (docs/MESHBAY_DESIGN.md §6.5's
+ enrichment rule:
the client asks, the node produces on demand, the asking device
caches — nothing durable here).
@@ -5895,13 +5898,14 @@ class WebRTCPeerSession:
# anything failing loudly: a file uploaded from a phone could not be
# deleted from the same person's laptop, and the only symptom was
# "Signature verification failed" on their own file
- # (docs/desktop-client-v1.md §4.8 A).
+ # (docs/MESHBAY_DESIGN.md §3.3).
#
# `uploader_pk` is kept, and stops being the authorization key: it is
# now the audit record of *which device* did it. Authorization is by
# account, through the roster — never through a token claim, which is
- # the protection `per-node-identity-v1.md` added and which a lookup by
- # `uploader_id` in the hub's world would give straight back.
+ # the protection per-node identity keys give (docs/MESHBAY_DESIGN.md
+ # §3.2) and which a lookup by `uploader_id` in the hub's world would
+ # give straight back.
if not (await self._verify_admin_sig(transcript, sig)
or await self._verify_uploader_sig(entry, transcript, sig)):
self._send({"type": "error", "detail": "Signature verification failed"})
@@ -5915,7 +5919,7 @@ class WebRTCPeerSession:
) -> None:
# Node operator only. A group admin who does not run the node has no
# authority over who this node admits (deny by default). Delegation is
- # designed but deferred — see §6.2 of docs/invite-pairing-v1.md.
+ # designed but deferred — see docs/MESHBAY_DESIGN.md §3.4.
if not await self._verify_admin_sig(transcript, sig):
self._send({"type": "error", "detail": "Signature verification failed"})
self._audit("admin_auth_failed", f"invite_create:{pending['subject'][:16]}")
@@ -6220,7 +6224,7 @@ class WebRTCPeerSession:
# node's answer to it, since ffmpeg re-encodes these in real time on
# any machine that can run this daemon. Reported live against an
# Xvid/MP3 .avi. `transcode_incompatible_video`'s own documentation
- # (draft-v6 §2.11) already said "HEVC *and other browser-
+ # (docs/MESHBAY_DESIGN.md §6.8) already said "HEVC *and other browser-
# incompatible video codecs*"; only HEVC was ever wired up.
can_copy = (bool(codec_str)
and raw_video_codec not in BROWSER_INCOMPATIBLE_VIDEO_CODECS)
@@ -6906,9 +6910,9 @@ class WebRTCTransport:
`ctx["_transcode_sem"]`, and `hasattr(webrtc, "_stream_sem")` is always
False. So the hot-swap was a no-op and **`max_concurrent_streams` has
never taken effect from the Node page without a restart**, contrary to
- draft-v6 §2.11. This is the one implementation, on the object that owns
- the state, so the next two caps do not each grow their own copy of the
- mistake.
+ docs/MESHBAY_DESIGN.md §6.8. This is the one implementation, on the
+ object that owns the state, so the next two caps do not each grow their
+ own copy of the mistake.
What resizing means, stated because it is a decision and not a
detail: **the new cap governs new streams; the ones already running are