""" MeshBay protocol constants and message type definitions. MNP (Mesh Node Protocol) — v0.1 MHP (Mesh Bay Hub Protocol) — v0.1 All wire messages are length-prefixed msgpack (4-byte big-endian length header). Every message carries a "v" field for protocol version. """ from dataclasses import dataclass, field from typing import Any # The wire versions live in meshbay_common/__init__.py — one source, because a # second copy here said "0.1" while every message on the wire carried "0.2". # Nothing imported it, which is the only reason it was harmless. from meshbay_common import MNP_VERSION, MHP_VERSION # noqa: F401 (re-export) # ── MNP message types ───────────────────────────────────────────────────────── class MNP: HANDSHAKE = "handshake" HANDSHAKE_ACK = "handshake_ack" INDEX_SYNC = "index_sync" # full Mesh Group Index INDEX_DELTA = "index_delta" # incremental update # Node -> already-connected members: "the operator's node is scanning # right now, N/M bytes done". Never the entries themselves (that is # INDEX_SYNC/INDEX_DELTA's job) — just enough for a presence dot to # animate. Pushed periodically while scanning, and once more on the # transition back to idle, so the indicator is guaranteed to turn off. INDEX_PROGRESS = "index_progress" FILE_REQUEST = "file_req" # request chunk(s) FILE_CHUNK = "file_chunk" # encrypted chunk response STREAM_SEGMENT = "stream_seg" # HLS/DASH segment CHAT_MESSAGE = "chat_msg" # Double Ratchet message CHAT_ATTACHMENT = "chat_attach" # attachment metadata CHAT_HISTORY = "chat_hist" # request message history (newest, or before a cursor) CHAT_HISTORY_RESPONSE = "chat_hist_resp" # history response with messages # Liveness on an *already open* channel. A peer that goes away without # closing leaves a DataChannel that still reads as connected until the next # real request hangs, and there was no way to ask. This is not a discovery # mechanism: opening a connection in order to ping costs a full ICE/DTLS # handshake (measured at 0.6-7 s across two ISPs), so presence in the group # list comes from the hub's socket registry instead. PING = "ping" PONG = "pong" # GEK_REQUEST / GEK_RESPONSE were removed (NS3, and finding L1): the node must # never serve the GEK in plaintext. Members obtain it by unwrapping their own # ECIES bundle. The constants lingered after the handlers were deleted, leaving # the wire contract looking as though the endpoint still existed. FILE_UPLOAD = "file_upload" # client pushes file chunk to node FILE_UPLOAD_ACK = "file_upload_ack" # node acknowledges chunk receipt DIR_CREATE = "dir_create" # client → node: make a directory DIR_CREATE_ACK = "dir_create_ack" # node → client: created DIR_DELETE = "dir_delete" # client → node: remove an empty directory DIR_DELETE_ACK = "dir_delete_ack" # node → client: removed FILE_DELETE = "file_delete" # client requests file deletion FILE_DELETE_ACK = "file_delete_ack" # node confirms deletion STREAM_REQUEST = "stream_req" # client requests MSE video stream STREAM_INIT = "stream_init" # node sends codec info + signals stream start STREAM_DATA = "stream_data" # node sends encrypted fMP4 segment STREAM_END = "stream_end" # node signals end of stream STREAM_MORE = "stream_more" # client → node: room for N more segments STREAM_STOP = "stream_stop" # client → node: nobody is watching any more EPHEMERAL_STREAM = "ephemeral_stream" # reserved — mobile live push HANDSHAKE_CHALLENGE = "handshake_challenge" # node → client: GEK proof nonce HANDSHAKE_RESPONSE = "handshake_response" # client → node: HMAC(GEK, nonce) ADMIN_CHALLENGE = "admin_challenge" # node → client: Ed25519 sign challenge ADMIN_RESPONSE = "admin_response" # client → node: Ed25519 signature # GEK_BUNDLE_STORE was removed with the invite redesign: the node wraps the GEK # itself, for a key the recipient proved possession of, so no member ever hands # the node key material (C5b, and the H3 substitution it enabled). GEK_BUNDLE_FETCH = "gek_bundle_fetch" # client → node: request own wrapped GEK GEK_BUNDLE_RESP = "gek_bundle_resp" # node → client: wrapped GEK bundle KEYPAIR_BUNDLE_STORE = "keypair_bundle_store" # client → node: store encrypted keypair bundle KEYPAIR_BUNDLE_FETCH = "keypair_bundle_fetch" # client → node: request own keypair bundle KEYPAIR_BUNDLE_RESP = "keypair_bundle_resp" # node → client: encrypted keypair bundle KEYPAIR_BUNDLE_DELETE = "keypair_bundle_delete" # client → node: withdraw own backup JOIN_REQUEST = "join_request" # client → node: pair/recognise this identity JOIN_RESULT = "join_result" # node → client: outcome + wrapped GEK INVITE_CREATE = "invite_create" # operator → node: issue a pairing code MEMBER_REVOKE = "member_revoke" # operator → node: stop serving the key MEMBER_REVOKE_ACK = "member_revoke_ack" MEMBER_UNPIN = "member_unpin" # operator → node: forget an identity MEMBER_UNPIN_ACK = "member_unpin_ack" MEMBER_UPLOAD = "member_upload" # operator → node: may members upload? MEMBER_UPLOAD_ACK = "member_upload_ack" APPS_ENABLED = "apps_enabled" # operator → node: which group apps to show APPS_ENABLED_ACK = "apps_enabled_ack" SET_SCAN_SETTINGS = "set_scan_settings" # operator → node: reconcile/debounce timing SET_SCAN_SETTINGS_ACK = "set_scan_settings_ack" MEDIA_META_REQ = "media_meta_req" # client → node: TMDB metadata for a path MEDIA_META_RESP = "media_meta_resp" # node → client: TMDB metadata (or none) VIDEO_ROOT = "video_root" # operator → node: which folder is the Videos entry point VIDEO_ROOT_ACK = "video_root_ack" TMDB_CONFIG = "tmdb_config" # operator → node: set custom TMDB token/language (node-wide) TMDB_CONFIG_ACK = "tmdb_config_ack" # node → everyone: new TMDB config (never the token) TMDB_ENABLED = "tmdb_enabled" # operator → node: enable/disable TMDB for this group TMDB_ENABLED_ACK = "tmdb_enabled_ack" # node → this group: new per-group TMDB enabled state SEASON_META_REQ = "season_meta_req" # client → node: TMDB overview/poster for one season SEASON_META_RESP = "season_meta_resp" # node → client: season-level TMDB fields (or none) TMDB_SEARCH_REQ = "tmdb_search_req" # client → node: candidate TMDB matches for a query TMDB_SEARCH_RESP = "tmdb_search_resp" # node → client: candidate list (id, title, year, poster) TMDB_OVERRIDE = "tmdb_override" # operator → node: replace a show/movie's TMDB match TMDB_OVERRIDE_ACK = "tmdb_override_ack" # Music app (docs/musicbay.md) — same shape as the TMDB pair above, minus # a credential: MusicBrainz read lookups need no API key, only a # rate-limited, self-identifying client (§3 there). MUSICBRAINZ_CONFIG = "musicbrainz_config" # operator → node: contact string MUSICBRAINZ_CONFIG_ACK = "musicbrainz_config_ack" # node → everyone: new config (no secret) MUSICBRAINZ_ENABLED = "musicbrainz_enabled" # operator → node: enable/disable MUSICBRAINZ_ENABLED_ACK = "musicbrainz_enabled_ack" # node → this group: new enabled state MUSIC_META_REQ = "music_meta_req" # client → node: metadata for a path MUSIC_META_RESP = "music_meta_resp" # node → client: metadata (or none) # A file whose format the browser's own