meshsocket 0.4.0__tar.gz → 0.4.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshsocket
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Lightweight WebSocket mesh networking — broadcast, direct routing, and request/response over a simple JSON wire protocol.
5
5
  Author: Carter Beaudoin
6
6
  License-Expression: MIT
@@ -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.0"
10
+ __version__ = "0.4.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshsocket
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Lightweight WebSocket mesh networking — broadcast, direct routing, and request/response over a simple JSON wire protocol.
5
5
  Author: Carter Beaudoin
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshsocket"
7
- version = "0.4.0"
7
+ version = "0.4.1"
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`, overwriting any value the
30
- sender wrote. Only a dict payload is stamped. On an E2EE envelope
31
- (`{"e2ee": 2, "s", "n", "ct"}`) the key lands on the outer, unsealed object
32
- beside the ciphertext, which is where receivers read the relay's stamp; the
33
- sealed body is never touched. Other payloads (lists, strings, null) pass
34
- unchanged. Returns a copy; the caller's dict is not modified."""
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():
@@ -503,7 +525,8 @@ class MeshServer:
503
525
  # The target never answered: say so instead of leaving the
504
526
  # requester to wait out its own timeout with no signal.
505
527
  return {"error": "target timeout", "status": "failed"}
506
- return response
528
+ # The target wrote this reply: it may not carry the relay-only key.
529
+ return strip_relay_sender(response)
507
530
 
508
531
  @client.on("get_nodes")
509
532
  async def on_get_nodes(payload):
@@ -635,8 +658,11 @@ class MeshServer:
635
658
  # `command_ack` whose `to` differs, and carterkit acks to the outer `_from`
636
659
  # of a sealed frame when there is one. Behind the gateway the relay name is
637
660
  # namespaced, so stamping broadcasts would break those acks.
661
+ # A peer-written RELAY_SENDER_KEY is removed (0.4.1): on a broadcast the
662
+ # receiver finds none, so it never mistakes a peer's claim for the relay's.
638
663
  if not self.clients:
639
664
  return
665
+ payload = strip_relay_sender(payload)
640
666
  sends, targets = [], []
641
667
  for peer in list(self.clients.values()):
642
668
  if sender and not self._can_receive_broadcast(sender, peer):
File without changes
File without changes
File without changes
File without changes