blob: 08dc47bc3a396f8d2632ae964c22ee57129da6a7 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
"""
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
DIR_CREATE = "dir_create" # client → node: make a directory
DIR_CREATE_ACK = "dir_create_ack" # node → client: created
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 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
INVITE_RESULT = "invite_result" # node → operator: the code, once
# ── 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
|