meshsocket 0.4.0__tar.gz → 0.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meshsocket-0.4.0 → meshsocket-0.4.3}/PKG-INFO +1 -1
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket/__init__.py +1 -1
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket.egg-info/PKG-INFO +1 -1
- {meshsocket-0.4.0 → meshsocket-0.4.3}/pyproject.toml +1 -1
- {meshsocket-0.4.0 → meshsocket-0.4.3}/socket_server.py +40 -9
- {meshsocket-0.4.0 → meshsocket-0.4.3}/LICENSE +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/README.md +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket.egg-info/SOURCES.txt +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket.egg-info/dependency_links.txt +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket.egg-info/requires.txt +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/meshsocket.egg-info/top_level.txt +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/setup.cfg +0 -0
- {meshsocket-0.4.0 → meshsocket-0.4.3}/socketCore.py +0 -0
|
@@ -7,4 +7,4 @@ so existing code keeps running while it migrates to the package name.
|
|
|
7
7
|
from socketCore import MeshSocket, LogColors, sanitize_identity, valid_ident, IDENT_RE, NAMESPACED_RE
|
|
8
8
|
|
|
9
9
|
__all__ = ["MeshSocket", "LogColors", "sanitize_identity", "valid_ident", "IDENT_RE", "NAMESPACED_RE"]
|
|
10
|
-
__version__ = "0.4.
|
|
10
|
+
__version__ = "0.4.3"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "meshsocket"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.3"
|
|
8
8
|
description = "Lightweight WebSocket mesh networking — broadcast, direct routing, and request/response over a simple JSON wire protocol."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -22,23 +22,45 @@ class MeshServerConfigError(RuntimeError):
|
|
|
22
22
|
# The key the relay writes on every routed payload (0.4.0): the sender's
|
|
23
23
|
# authenticated name, as this relay registered it at identify (through the
|
|
24
24
|
# carter-relay gateway that is the namespaced `<account>.<principal>_<name>`).
|
|
25
|
+
# Peers write `_from` too (on broadcasts, where it is left alone), so a receiver
|
|
26
|
+
# cannot tell this stamp from a peer's claim by the key alone: see RELAY_SENDER_KEY.
|
|
25
27
|
SENDER_KEY = "_from"
|
|
26
28
|
|
|
29
|
+
# The relay-only sender key (0.4.1). Set beside `_from` on every routed payload,
|
|
30
|
+
# and removed from every payload the relay forwards (routed, routed replies and
|
|
31
|
+
# broadcasts), so a receiver that finds it knows this relay wrote it: no peer can
|
|
32
|
+
# get its own copy through. Broadcasts never carry it (they are not stamped).
|
|
33
|
+
RELAY_SENDER_KEY = "_relayFrom"
|
|
34
|
+
|
|
27
35
|
|
|
28
36
|
def stamp_sender(payload: Any, sender_name: str) -> Any:
|
|
29
|
-
"""`payload` with `SENDER_KEY` set to `sender_name`,
|
|
30
|
-
sender wrote. Only a dict payload is stamped. On an
|
|
31
|
-
(`{"e2ee": 2, "s", "n", "ct"}`) the
|
|
32
|
-
beside the ciphertext, which is where receivers read the
|
|
33
|
-
sealed body is never touched. Other payloads (lists,
|
|
34
|
-
unchanged. Returns a copy; the caller's dict is not
|
|
37
|
+
"""`payload` with `SENDER_KEY` and `RELAY_SENDER_KEY` set to `sender_name`,
|
|
38
|
+
overwriting any value the sender wrote. Only a dict payload is stamped. On an
|
|
39
|
+
E2EE envelope (`{"e2ee": 2, "s", "n", "ct"}`) the keys land on the outer,
|
|
40
|
+
unsealed object beside the ciphertext, which is where receivers read the
|
|
41
|
+
relay's stamp; the sealed body is never touched. Other payloads (lists,
|
|
42
|
+
strings, null) pass unchanged. Returns a copy; the caller's dict is not
|
|
43
|
+
modified."""
|
|
35
44
|
if not isinstance(payload, dict):
|
|
36
45
|
return payload
|
|
37
46
|
stamped = dict(payload)
|
|
38
47
|
stamped[SENDER_KEY] = sender_name
|
|
48
|
+
stamped[RELAY_SENDER_KEY] = sender_name
|
|
39
49
|
return stamped
|
|
40
50
|
|
|
41
51
|
|
|
52
|
+
def strip_relay_sender(payload: Any) -> Any:
|
|
53
|
+
"""`payload` without a peer-written `RELAY_SENDER_KEY` on its outer object.
|
|
54
|
+
Only a dict payload changes (a copy; the caller's dict is not modified);
|
|
55
|
+
nested values and an E2EE envelope's sealed body are not touched, and
|
|
56
|
+
everything else, `_from` included, is delivered as sent."""
|
|
57
|
+
if not isinstance(payload, dict) or RELAY_SENDER_KEY not in payload:
|
|
58
|
+
return payload
|
|
59
|
+
stripped = dict(payload)
|
|
60
|
+
del stripped[RELAY_SENDER_KEY]
|
|
61
|
+
return stripped
|
|
62
|
+
|
|
63
|
+
|
|
42
64
|
def _env_int(name: str, default: int) -> int:
|
|
43
65
|
raw = os.getenv(name)
|
|
44
66
|
if raw is None or not raw.strip():
|
|
@@ -478,13 +500,18 @@ class MeshServer:
|
|
|
478
500
|
if not isinstance(payload, dict):
|
|
479
501
|
return {"error": "malformed route", "status": "failed"}
|
|
480
502
|
target_id = payload.get('target_id')
|
|
503
|
+
target_name = payload.get('target_name')
|
|
481
504
|
msg_type = payload.get('type')
|
|
482
505
|
# The target learns who sent this from the relay, not the sender.
|
|
483
506
|
data = stamp_sender(payload.get('payload'), client.name)
|
|
484
|
-
|
|
507
|
+
# Address by id, or by name like route_msg_noreply (the app's hub-audio
|
|
508
|
+
# requests name the hub: CAR-TER aif-6e55l, spec "Wire contract").
|
|
509
|
+
by_id = isinstance(target_id, str)
|
|
510
|
+
address = target_id if by_id else target_name
|
|
511
|
+
if not isinstance(msg_type, str) or not isinstance(address, str):
|
|
485
512
|
return {"error": "malformed route", "status": "failed"}
|
|
486
513
|
|
|
487
|
-
target = self.clients.get(
|
|
514
|
+
target = self.clients.get(address) if by_id else self.clients_by_name.get(address)
|
|
488
515
|
if not target:
|
|
489
516
|
return {"error": "Target not found", "status": "failed"}
|
|
490
517
|
if not client.can_route:
|
|
@@ -503,7 +530,8 @@ class MeshServer:
|
|
|
503
530
|
# The target never answered: say so instead of leaving the
|
|
504
531
|
# requester to wait out its own timeout with no signal.
|
|
505
532
|
return {"error": "target timeout", "status": "failed"}
|
|
506
|
-
|
|
533
|
+
# The target wrote this reply: it may not carry the relay-only key.
|
|
534
|
+
return strip_relay_sender(response)
|
|
507
535
|
|
|
508
536
|
@client.on("get_nodes")
|
|
509
537
|
async def on_get_nodes(payload):
|
|
@@ -635,8 +663,11 @@ class MeshServer:
|
|
|
635
663
|
# `command_ack` whose `to` differs, and carterkit acks to the outer `_from`
|
|
636
664
|
# of a sealed frame when there is one. Behind the gateway the relay name is
|
|
637
665
|
# namespaced, so stamping broadcasts would break those acks.
|
|
666
|
+
# A peer-written RELAY_SENDER_KEY is removed (0.4.1): on a broadcast the
|
|
667
|
+
# receiver finds none, so it never mistakes a peer's claim for the relay's.
|
|
638
668
|
if not self.clients:
|
|
639
669
|
return
|
|
670
|
+
payload = strip_relay_sender(payload)
|
|
640
671
|
sends, targets = [], []
|
|
641
672
|
for peer in list(self.clients.values()):
|
|
642
673
|
if sender and not self._can_receive_broadcast(sender, peer):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|