""" 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 MNP_VERSION = "0.1" MHP_VERSION = "0.1" # ── 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 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 CHAT_HISTORY_RESPONSE = "chat_hist_resp" # history response with messages # 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 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 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 = "gek_bundle_store" # client → node: store wrapped GEK for a user 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 # ── Index entry ─────────────────────────────────────────────────────────────── @dataclass class IndexEntry: id: str # blake3 hash of file (hex) name: str # filename path: str # path relative to shared directory size: int # bytes type: str # video | audio | image | document | archive | other added_at: int # unix timestamp duration: int | None = None # seconds, for media thumb_hash: str | None = None # blake3 of thumbnail uploader_id: str | None = None # user_id of who uploaded (None = pre-existing on disk) uploader_pk: str | None = None # Ed25519 public key of uploader (base64 raw 32 bytes) @dataclass class IndexDelta: base_version: int version: int additions: list[IndexEntry] = field(default_factory=list) deletions: list[str] = field(default_factory=list) # list of ids # ── Chunk request/response ──────────────────────────────────────────────────── @dataclass class ChunkRequest: file_id: str # blake3 hash of file (hex) chunk_index: int @dataclass class ChunkResponse: chunk_index: int plaintext_size: int nonce_b64: str ct_b64: str ct_hash_b64: str pt_hash_b64: str sig_b64: str pk_node_b64: str file_hash_b64: str